from __future__ import annotations

from decimal import Decimal

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 (
    confirm_migros_receipts_2026,
    list_transaction_candidates,
    preview_migros_receipts_2026,
    seed_credit_card_candidates_from_rows,
    seed_migros_candidates_from_rows,
)
from jarvis_finance.services.budget_overview import get_budget_status_by_category, get_category_month_detail
from jarvis_finance.services.budget_plans import confirm_budget_plan_item
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 base(conn):
    account = confirm_create_budget_account(conn, {"name": "Budgetkonto", "account_type": "cash", "currency": "CHF"})["entity_id"]
    food = confirm_create_category(conn, {"name": "Essen & Haushalt", "category_type": "expense", "sort_order": 10})["entity_id"]
    other = confirm_create_category(conn, {"name": "Sonstiges / Administration", "category_type": "expense", "sort_order": 90})["entity_id"]
    return account, food, other


def test_migros_receipts_are_always_food_household_and_can_be_confirmed_with_audit_without_article_transactions() -> None:
    conn = db(); account, food, _other = base(conn)
    rows = [
        {"Datum": "03.01.2026", "Zeit": "10:00", "Filiale": "Migros Test", "Kasse": "1", "Transaktion": "A", "Artikel": "Brot", "Umsatz": "30.00"},
        {"Datum": "03.01.2026", "Zeit": "10:00", "Filiale": "Migros Test", "Kasse": "1", "Transaktion": "A", "Artikel": "Haushalt", "Umsatz": "31.00"},
        {"Datum": "04.01.2026", "Zeit": "11:00", "Filiale": "Migros Test", "Kasse": "2", "Transaktion": "B", "Artikel": "Milch", "Umsatz": "12.00"},
    ]
    seed = seed_migros_candidates_from_rows(conn, rows, source_file_label="migros.csv", auto_category_id=food, threshold_chf="50.00")
    assert seed["receipt_candidate_count"] == 2
    candidates = list_transaction_candidates(conn, source_type="migros_receipt", include_reference=True)
    assert len(candidates) == 2
    assert {c["proposed_category_id"] for c in candidates} == {food}
    assert {c["status"] for c in candidates} == {"auto_categorized"}
    assert all(c["requires_review"] is False for c in candidates)
    assert all(c["classification"] == "migros_receipt_food_household" for c in candidates)

    preview = preview_migros_receipts_2026(conn)
    assert preview["receipt_candidate_count"] == 2
    assert preview["open_receipt_count"] == 2
    assert preview["line_item_count"] == 3
    assert preview["covered_by_migros_count"] == 0
    assert "amount" not in preview

    confirmed = confirm_migros_receipts_2026(conn, account_id=account)
    assert confirmed["confirmed_count"] == 2
    assert confirmed["line_item_transaction_count"] == 0
    assert confirmed["audit_id"]
    tx_rows = conn.execute("SELECT * FROM budget_transactions WHERE source_type='import_candidate'").fetchall()
    assert len(tx_rows) == 2
    assert {row["category_id"] for row in tx_rows} == {food}
    assert all("Brot" not in row["description"] and "Milch" not in row["description"] for row in tx_rows)
    assert conn.execute("SELECT COUNT(*) FROM budget_import_line_items").fetchone()[0] == 3
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action IN ('migros_receipts_2026_confirmed','candidate_confirmed')").fetchone()[0] >= 1


def test_credit_card_migros_stays_covered_and_is_not_confirmed_as_duplicate_expense() -> None:
    conn = db(); account, food, _other = base(conn)
    seed_migros_candidates_from_rows(conn, [{"Datum": "05.01.2026", "Zeit": "10:00", "Filiale": "Migros Test", "Kasse": "1", "Transaktion": "A", "Artikel": "Bon", "Umsatz": "44.00"}], source_file_label="migros.csv", auto_category_id=food)
    seed_credit_card_candidates_from_rows(conn, [{"Datum": "05.01.2026", "Beschreibung": "MIGROS MMM", "Betrag": "44.00"}], source_file_label="card.csv", migros_covered=True)

    preview = preview_migros_receipts_2026(conn)
    assert preview["covered_by_migros_count"] == 1
    confirm_migros_receipts_2026(conn, account_id=account)
    assert conn.execute("SELECT COUNT(*) FROM budget_transactions WHERE description LIKE '%MIGROS MMM%'").fetchone()[0] == 0
    covered = list_transaction_candidates(conn, status="covered_by_migros", include_reference=True)
    assert len(covered) == 1
    assert covered[0]["status"] == "covered_by_migros"


def test_budgetstatus_monthly_analytics_and_month_detail_groupings() -> None:
    conn = db(); account, food, other = base(conn)
    confirm_budget_plan_item(conn, {"plan_month": "2026-01", "category_id": food, "name": "Food plan", "monthly_amount_chf": "100.00", "cadence": "monthly"})
    seed_migros_candidates_from_rows(conn, [
        {"Datum": "10.01.2026", "Zeit": "10:00", "Filiale": "Migros A", "Kasse": "1", "Transaktion": "A", "Artikel": "Bon", "Umsatz": "120.00"},
        {"Datum": "11.02.2026", "Zeit": "10:00", "Filiale": "Migros B", "Kasse": "1", "Transaktion": "B", "Artikel": "Bon", "Umsatz": "40.00"},
    ], source_file_label="migros.csv", auto_category_id=food)
    confirm_migros_receipts_2026(conn, account_id=account)

    rows = get_budget_status_by_category(conn, year="2026")
    food_row = next(r for r in rows if r["category_id"] == food)
    jan = next(m for m in food_row["monthly_analysis"] if m["month"] == "2026-01")
    feb = next(m for m in food_row["monthly_analysis"] if m["month"] == "2026-02")
    assert jan["actual_chf"] == "120.00"
    assert jan["budget_chf"] == "100.00"
    assert jan["status"] == "Rot"
    assert jan["bar_color"] == "red"
    assert feb["actual_chf"] == "40.00"
    assert feb["status"] == "Grün"
    assert feb["bar_color"] == "green"

    detail = get_category_month_detail(conn, category_id=food, month="2026-01")
    assert detail["category_id"] == food
    assert detail["month"] == "2026-01"
    assert detail["budget_month"] == "100.00"
    assert detail["actual_chf"] == "120.00"
    assert detail["difference_chf"] == "20.00"
    assert Decimal(detail["difference_percent"]) == Decimal("20.00")
    assert detail["status"] == "Rot"
    assert detail["breakdown"]
    assert detail["breakdown_mode"] in {"merchant", "source"}
    assert len(detail["transactions"]) == 1
    assert detail["transactions"][0]["category_id"] == food
    assert detail["transactions"][0]["line_item_count"] == 1
    assert "expense_filter_url" in detail
    assert detail["expense_filter_url"].startswith("/household/transactions?")
    assert "period=2026-01" in detail["expense_filter_url"]
    assert f"category_id={food}" in detail["expense_filter_url"]
    assert "financial_effect=expense" in detail["expense_filter_url"]
