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_planning import (
    confirm_budget_planning_batch,
    confirm_category_budget_values,
    get_budget_planning_audit_timeline,
    get_budget_planning_matrix,
    preview_budget_planning_batch,
    preview_budget_planning_excel_template,
    preview_category_budget_values,
)
from jarvis_finance.services.budget_transactions import confirm_budget_transaction
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_two_categories(conn):
    account_id = confirm_create_budget_account(conn, {"name": "Haushalt", "account_type": "checking", "currency": "CHF"})["entity_id"]
    media = confirm_create_category(conn, {"name": "Elektronische Medien", "category_type": "expense"})["entity_id"]
    food = confirm_create_category(conn, {"name": "Essen & Haushalt", "category_type": "expense"})["entity_id"]
    return account_id, media, food


def book_expense(conn, account_id: str, category_id: str, amount: str):
    confirm_budget_transaction(conn, {"account_id": account_id, "transaction_type": "expense", "transaction_date": "2026-02-10", "description": "confirmed", "amount_original": amount, "currency_original": "CHF", "category_id": category_id})


def test_audit_timeline_shows_readable_field_changes_for_budget_and_previous_year() -> None:
    conn = db(); _account, media, _food = setup_two_categories(conn)
    confirm_category_budget_values(conn, media, preview_category_budget_values(conn, media, {"monthly_budget_chf": "100.00", "previous_year_total_chf": "960.00", "notes": "initial"})["payload"])
    confirm_category_budget_values(conn, media, preview_category_budget_values(conn, media, {"monthly_budget_chf": "120.00", "previous_year_months": {"01": "90.00", "02": "110.00"}, "notes": "changed"})["payload"])

    timeline = get_budget_planning_audit_timeline(conn, media)

    assert timeline["category_id"] == media
    assert len(timeline["entries"]) >= 2
    latest = timeline["entries"][0]
    assert latest["action_label"] == "Budgetplanung gespeichert"
    assert any(change["field_label"] == "Budget Monat" and change["new_value"] == "120.00" for change in latest["changes"])
    assert any(change["field_label"] == "Vorjahr Monatswerte" for change in latest["changes"])
    assert latest["source"] == "vue_dashboard"
    assert latest["created_by"] == "user"
    assert "audit_id" in latest["technical"]


def test_batch_preview_and_confirm_collect_multiple_budget_changes_with_audit() -> None:
    conn = db(); _account, media, food = setup_two_categories(conn)
    preview = preview_budget_planning_batch(conn, {"changes": [
        {"category_id": media, "monthly_budget_chf": "50.00", "budget_cadence": "monthly"},
        {"category_id": food, "annual_budget_chf": "1200.00", "budget_cadence": "annual", "previous_year_total_chf": "1000.00", "distribute_previous_year_total": True},
    ]})

    assert preview["count"] == 2
    assert preview["requires_explicit_confirm"] is True
    assert preview["changes"][0]["review"]["budget_year_chf"] == "600.00"
    assert preview["changes"][1]["review"]["budget_month_chf"] == "100.00"

    result = confirm_budget_planning_batch(conn, preview["payload"])

    assert result["status"] == "confirmed"
    assert result["confirmed_count"] == 2
    rows = get_budget_planning_matrix(conn, year="2026", current_month="2026-02")["rows"]
    by_id = {row["category_id"]: row for row in rows}
    assert by_id[media]["budget_year_chf"] == "600.00"
    assert by_id[food]["previous_year_total_chf"] == "1000.00"
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action='budget_planning_batch_confirmed'").fetchone()[0] == 1


def test_batch_preview_blocks_invalid_negative_values_before_confirm() -> None:
    conn = db(); _account, media, _food = setup_two_categories(conn)
    with pytest.raises(HTTPException):
        preview_budget_planning_batch(conn, {"changes": [{"category_id": media, "monthly_budget_chf": "-1.00"}]})


def test_plausibility_checks_surface_budget_vs_previous_year_actual_and_forecast_risks() -> None:
    conn = db(); account, media, _food = setup_two_categories(conn)
    confirm_category_budget_values(conn, media, preview_category_budget_values(conn, media, {"annual_budget_chf": "100.00", "previous_year_total_chf": "1000.00"})["payload"])
    book_expense(conn, account, media, "150.00")

    row = next(r for r in get_budget_planning_matrix(conn, year="2026", current_month="2026-02")["rows"] if r["category_id"] == media)

    messages = " ".join(item["message"] for item in row["plausibility_checks"])
    severities = {item["severity"] for item in row["plausibility_checks"]}
    assert "mehr als 20% unter Vorjahr" in messages
    assert "Ist 2026 liegt bereits über Budget" in messages
    assert "Forecast liegt kritisch über Budget" in messages
    assert "warning" in severities and "critical" in severities


def test_excel_template_preview_is_prepared_without_productive_mutation() -> None:
    conn = db(); _account, _media, _food = setup_two_categories(conn)
    before_plans = conn.execute("SELECT COUNT(*) FROM budget_plan_items").fetchone()[0]
    preview = preview_budget_planning_excel_template(conn, {"year": "2026"})
    after_plans = conn.execute("SELECT COUNT(*) FROM budget_plan_items").fetchone()[0]

    assert preview["status"] == "prepared"
    assert preview["productive_mutation"] is False
    assert "Aus Excel-Vorlage übernehmen" in preview["title"]
    assert before_plans == after_plans
