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.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",
        "IBAN;Booked At;Text;Credit/Debit Amount;Balance;Valuta Date\nCH1280808006766055632;2026-01-26;Einkauf TWINT SWISSLOS E-COMMERCE;-100.00;0;2026-01-26\n",
        source_file_label="260518_Konto_Raiffeisen_20260518085941.csv",
        dry_run=False,
    )
    import_budget_csv_text(
        conn,
        "akb_bank",
        "Buchung;Valuta;Buchungstext;Belastung;Gutschrift;Saldo CHF\n26.01.2026;26.01.2026;TWINT-Zahlung SWISSLOS E-COMMERCE, BASEL;100.00;;0\n",
        source_file_label="Kontoauszug_360 Tage_AKB.csv",
        dry_run=False,
    )


def test_swisslos_raiffeisen_candidate_is_transfer_and_not_normal_review_expense():
    conn = db(); setup_base(conn); seed_swisslos(conn)
    result = classify_swisslos_internal_transfers(conn)

    assert result["updated_count"] == 1
    normal = list_transaction_candidates(conn, budget_year="2026")
    transfers = list_transaction_candidates(conn, budget_year="2026", tab="transfers", include_reference=True)
    assert all("SWISSLOS" not in r["description"] for r in normal)
    row = next(r for r in transfers if r["source_type"] == "raiffeisen_bank")
    assert row["status"] == "transfer_candidate"
    assert row["classification"] == "transfer_candidate"
    assert row["proposed_category_id"] is None
    assert row["rule_name"] == "Raiffeisen TWINT Swisslos → AKB Haushaltskonto Transfer"
    assert row["budget_impact"] == "neutral"
    assert row["transfer_target_account_name"] == "AKB Haushaltskonto"
    stored_rule = conn.execute("SELECT target_action, notes FROM budget_import_rules WHERE rule_id='rule_raiffeisen_swisslos_akb_transfer'").fetchone()
    assert stored_rule["target_action"] == "transfer_candidate"
    assert "AKB Haushaltskonto" in stored_rule["notes"]


def test_transfer_candidate_confirm_creates_transfer_not_income_or_expense_and_batch_skips_it():
    conn = db(); raif, akb, _food = setup_base(conn); seed_swisslos(conn)
    classify_swisslos_internal_transfers(conn)
    candidate = next(r for r in list_transaction_candidates(conn, budget_year="2026", tab="transfers", include_reference=True) if r["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"

    with pytest.raises(HTTPException):
        confirm_transaction_candidate(conn, candidate["transaction_candidate_id"], {"account_id": raif, "transaction_type": "transfer", "budget_year": "2026"})

    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 list_budget_transfers(conn, year="2026")
    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='candidate_transfer_confirmed'").fetchone()[0] == 1


def test_unmatched_swisslos_transfer_remains_review_required():
    conn = db(); setup_base(conn)
    import_budget_csv_text(conn, "raiffeisen_bank", "IBAN;Booked At;Text;Credit/Debit Amount;Balance;Valuta Date\nCH1280808006766055632;2026-05-13;Einkauf TWINT SWISSLOS E-COMMERCE;-100.00;0;2026-05-13\n", source_file_label="raiffeisen.csv", dry_run=False)
    result = classify_swisslos_internal_transfers(conn)
    row = list_transaction_candidates(conn, budget_year="2026", tab="transfers", include_reference=True)[0]
    assert result["unmatched_count"] == 1
    assert row["transfer_pair_status"] == "unmatched"
    assert row["requires_review"] is True
