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_csv_imports import import_budget_csv_text
from jarvis_finance.services.budget_imports import (
    classify_swisslos_internal_transfers,
    confirm_transaction_candidate,
    list_transaction_candidates,
    preview_batch_confirm_candidates,
)
from jarvis_finance.services.budget_overview import get_budget_status_by_category
from jarvis_finance.services.budget_transactions import (
    list_budget_transactions,
    list_budget_transfers,
)
from jarvis_finance.services.transfer_pairing import list_transfer_pairs
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):
    raif = confirm_create_budget_account(
        conn,
        {"name": "Raiffeisen", "account_type": "cash", "currency": "CHF"},
    )["entity_id"]
    akb = confirm_create_budget_account(
        conn,
        {"name": "AKB Haushaltskonto", "account_type": "cash", "currency": "CHF"},
    )["entity_id"]
    food = confirm_create_category(
        conn,
        {"name": "Essen & Haushalt", "category_type": "expense"},
    )["entity_id"]
    return raif, akb, food


def seed_swisslos(conn):
    import_budget_csv_text(
        conn,
        "raiffeisen_bank",
        "Account;Booked At;Text;Credit/Debit Amount;Balance;Valuta Date\n"
        "OWN-RAIFFEISEN;2026-01-26;Einkauf TWINT SWISSLOS E-COMMERCE;-100.00;0;2026-01-26\n",
        source_file_label="synthetic-raiffeisen-run",
        dry_run=False,
    )
    import_budget_csv_text(
        conn,
        "akb_bank",
        "Buchung;Valuta;Buchungstext;Belastung;Gutschrift;Saldo CHF;Konto\n"
        "26.01.2026;26.01.2026;Eigener Kontoübertrag;;100.00;0;OWN-AKB\n",
        source_file_label="synthetic-akb-run",
        dry_run=False,
    )


def test_legacy_reclassify_entry_point_uses_generic_pairing() -> None:
    conn = db()
    setup_base(conn)
    seed_swisslos(conn)

    result = classify_swisslos_internal_transfers(conn)
    pair = next(item for item in list_transfer_pairs(conn) if item["status"] == "proposed")
    normal = list_transaction_candidates(conn, budget_year="2026")

    assert result["matcher_version"] == "transfer_pairing_v2"
    assert result["matched_count"] == 1
    assert all("SWISSLOS" not in row["description"] for row in normal)
    assert pair["source_account_name"] == "Raiffeisen"
    assert pair["target_account_name"] == "AKB Haushaltskonto"
    assert pair["budget_effect_chf"] == "0"
    assert "merchant_text_not_decisive" in pair["reason_codes"]
    assert conn.execute(
        "SELECT COUNT(*) FROM budget_import_rules WHERE rule_id='rule_raiffeisen_swisslos_akb_transfer'"
    ).fetchone()[0] == 0


def test_candidate_confirm_uses_pair_and_never_creates_income_or_expense() -> None:
    conn = db()
    raif, akb, _food = setup_base(conn)
    seed_swisslos(conn)
    candidate = next(
        row
        for row in list_transaction_candidates(
            conn,
            budget_year="2026",
            tab="transfers",
            include_reference=True,
        )
        if row["source_type"] == "raiffeisen_bank"
    )
    with pytest.raises(HTTPException) as blocked:
        preview_batch_confirm_candidates(
            conn,
            candidate_ids=[candidate["transaction_candidate_id"]],
            account_id=raif,
            budget_year="2026",
        )
    assert blocked.value.detail["conflicts"][0]["reason"] == "transfer_candidate_blocked"

    result = confirm_transaction_candidate(
        conn,
        candidate["transaction_candidate_id"],
        {
            "from_account_id": raif,
            "to_account_id": akb,
            "transaction_type": "transfer",
            "budget_year": "2026",
        },
    )

    assert result["status"] == "confirmed"
    assert result["budget_effect_chf"] == "0"
    assert len(list_budget_transfers(conn, year="2026")) == 1
    assert list_budget_transactions(conn, year="2026", transaction_type="expense") == []
    assert list_budget_transactions(conn, year="2026", transaction_type="income") == []
    assert len(list_budget_transactions(conn, year="2026", transaction_type="transfer")) == 2
    assert get_budget_status_by_category(conn, year="2026") == []
    assert conn.execute(
        "SELECT COUNT(*) FROM audit_log WHERE action='transfer_pair_confirmed'"
    ).fetchone()[0] == 1


def test_unmatched_swisslos_candidate_cannot_be_productively_confirmed() -> None:
    conn = db()
    raif, akb, _food = setup_base(conn)
    import_budget_csv_text(
        conn,
        "raiffeisen_bank",
        "Account;Booked At;Text;Credit/Debit Amount;Balance;Valuta Date\n"
        "OWN-RAIFFEISEN;2026-05-13;Einkauf TWINT SWISSLOS E-COMMERCE;-100.00;0;2026-05-13\n",
        source_file_label="synthetic-raiffeisen-only",
        dry_run=False,
    )
    pair = next(item for item in list_transfer_pairs(conn) if item["status"] == "unmatched")

    assert pair["target_candidate_id"] is None
    assert pair["quality_status"] == "unmatched"
    with pytest.raises(HTTPException) as blocked:
        confirm_transaction_candidate(
            conn,
            pair["source_candidate_id"],
            {
                "from_account_id": raif,
                "to_account_id": akb,
                "transaction_type": "transfer",
                "budget_year": "2026",
            },
        )
    assert blocked.value.status_code == 409
    assert conn.execute("SELECT COUNT(*) FROM budget_transactions").fetchone()[0] == 0
