from __future__ import annotations

import pytest
from fastapi import HTTPException

from jarvis_finance.services.budget_accounts import confirm_create_budget_account
from jarvis_finance.services.budget_categories import confirm_create_category
from jarvis_finance.services.budget_imports import (
    apply_budget_rules_to_open_candidates,
    batch_mark_candidates,
    confirm_candidate_split,
    create_budget_rule,
    create_review_category_for_candidate,
    list_budget_rules,
    list_transaction_candidates,
    preview_batch_confirm_candidates,
    review_candidate_summary,
    seed_credit_card_candidates_from_rows,
    seed_transaction_candidates_from_rows,
    test_budget_rule as run_budget_rule,
)
from jarvis_finance.services.budget_overview import get_budget_status_by_category
from jarvis_finance.services.budget_transactions import list_budget_transactions
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations


def db():
    conn = connect_memory()
    apply_migrations(conn)
    return conn


def setup_base(conn):
    account = confirm_create_budget_account(conn, {"name": "Review Konto", "account_type": "cash", "currency": "CHF"})["entity_id"]
    food = confirm_create_category(conn, {"name": "Essen", "category_type": "expense"})["entity_id"]
    subs = confirm_create_category(conn, {"name": "Freizeit/Abos", "category_type": "expense"})["entity_id"]
    health = confirm_create_category(conn, {"name": "Gesundheit", "category_type": "expense"})["entity_id"]
    return account, food, subs, health


def test_phase19_review_summary_filter_and_sort_counts() -> None:
    conn = db(); _account, food, subs, _health = setup_base(conn)
    seed_credit_card_candidates_from_rows(conn, [
        {"Datum": "2026-02-01", "Beschreibung": "NETFLIX", "Betrag": "10.00"},
        {"Datum": "2026-02-02", "Beschreibung": "Galaxus Zürich", "Betrag": "20.00"},
        {"Datum": "2026-02-03", "Beschreibung": "MIGROS MMM", "Betrag": "30.00"},
    ], source_file_label="phase19_card.csv", subscription_category_id=subs, migros_covered=True)
    seed_transaction_candidates_from_rows(conn, [{"Datum": "2026-02-04", "Beschreibung": "Unklar", "Betrag": "40.00"}], source_file_label="phase19_bank.csv")
    summary = review_candidate_summary(conn)
    assert summary["open_total"] == 3
    assert summary["galaxus_digitec"] == 1
    assert summary["covered_by_migros"] == 1
    assert summary["unclear"] >= 1
    rows = list_transaction_candidates(conn, source_type="credit_card_csv", merchant="net", sort_by="merchant", sort_dir="asc")
    assert [r["description"] for r in rows] == ["NETFLIX"]


def test_phase19_batch_preview_required_before_confirm_and_actions() -> None:
    conn = db(); account, _food, subs, _health = setup_base(conn)
    seed_credit_card_candidates_from_rows(conn, [{"Datum": "2026-02-01", "Beschreibung": "NETFLIX", "Betrag": "10.00"}], source_file_label="phase19_card.csv", subscription_category_id=subs)
    cid = list_transaction_candidates(conn)[0]["transaction_candidate_id"]
    preview = preview_batch_confirm_candidates(conn, candidate_ids=[cid], account_id=account)
    assert preview["count"] == 1
    assert preview["requires_explicit_confirm"] is True
    assert list_budget_transactions(conn) == []
    result = batch_mark_candidates(conn, candidate_ids=[cid], action="confirm", account_id=account, preview_id=preview["preview_id"])
    assert result["confirmed_count"] == 1
    assert list_budget_transactions(conn, status="confirmed")
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action IN ('batch_confirm_candidates_preview','batch_confirm_candidates')").fetchone()[0] == 2


def test_phase19_split_sum_blocks_and_confirm_creates_linked_transactions() -> None:
    conn = db(); account, food, subs, _health = setup_base(conn)
    seed_credit_card_candidates_from_rows(conn, [{"Datum": "2026-02-02", "Beschreibung": "Galaxus Zürich", "Betrag": "20.00"}], source_file_label="phase19_card.csv", subscription_category_id=subs)
    cid = list_transaction_candidates(conn, tab="galaxus")[0]["transaction_candidate_id"]
    with pytest.raises(HTTPException):
        confirm_candidate_split(conn, cid, {"account_id": account, "splits": [{"category_id": food, "amount_original": "9.00"}]})
    result = confirm_candidate_split(conn, cid, {"account_id": account, "splits": [
        {"category_id": food, "amount_original": "9.00", "note": "Teil A", "tag": "Split"},
        {"category_id": subs, "amount_original": "11.00", "note": "Teil B"},
    ]})
    assert result["created_transactions"] == 2
    assert len(list_budget_transactions(conn, status="confirmed")) == 2
    stored = conn.execute("SELECT status, confirmed_transaction_id FROM budget_transaction_candidates WHERE transaction_candidate_id=?", (cid,)).fetchone()
    assert stored["status"] == "confirmed"
    assert stored["confirmed_transaction_id"] == result["group_id"]


def test_phase19_category_from_review_and_rule_mvp_apply_only_suggestions() -> None:
    conn = db(); _account, _food, _subs, health = setup_base(conn)
    seed_transaction_candidates_from_rows(conn, [{"Datum": "2026-02-04", "Beschreibung": "Apotheke Test", "Betrag": "12.00"}], source_file_label="phase19.csv")
    cid = list_transaction_candidates(conn)[0]["transaction_candidate_id"]
    created = create_review_category_for_candidate(conn, cid, {"name": "Review Neue Kategorie", "category_type": "expense"})
    row = conn.execute("SELECT proposed_category_id FROM budget_transaction_candidates WHERE transaction_candidate_id=?", (cid,)).fetchone()
    assert row["proposed_category_id"] == created["category_id"]
    rule = create_budget_rule(conn, {"merchant_contains": "Apotheke", "source_type": "csv_seed", "category_id": health, "target_status": "review"})
    assert list_budget_rules(conn)[0]["is_active"] is True
    tested = run_budget_rule(conn, rule["rule_id"])
    assert tested["match_count"] >= 1
    applied = apply_budget_rules_to_open_candidates(conn, rule["rule_id"])
    assert applied["updated_count"] >= 1
    assert list_budget_transactions(conn) == []


def test_phase19_budgetstatus_row_click_backing_after_recategorise() -> None:
    conn = db(); account, food, subs, _health = setup_base(conn)
    seed_credit_card_candidates_from_rows(conn, [{"Datum": "2026-02-01", "Beschreibung": "NETFLIX", "Betrag": "10.00"}], source_file_label="phase19_card.csv", subscription_category_id=subs)
    cid = list_transaction_candidates(conn)[0]["transaction_candidate_id"]
    preview = preview_batch_confirm_candidates(conn, candidate_ids=[cid], account_id=account)
    batch_mark_candidates(conn, candidate_ids=[cid], action="confirm", account_id=account, preview_id=preview["preview_id"])
    tx = list_budget_transactions(conn, status="confirmed")[0]
    from jarvis_finance.services.budget_transactions import update_budget_transaction
    update_budget_transaction(conn, tx["budget_transaction_id"], {"category_id": food, "change_note": "phase19"})
    rows = get_budget_status_by_category(conn, year="2026")
    assert next(r for r in rows if r["category_id"] == food)["actual_year_to_date"] != "0.00"
