from __future__ import annotations

import hashlib
import json
from datetime import date
from decimal import Decimal, InvalidOperation
from sqlite3 import Connection
from typing import Any

from fastapi import HTTPException

from jarvis_finance.audit.log import record_audit_event
from jarvis_finance.services.budget_common import new_id, now

DEFAULT_COMPARISON_YEAR = "2025"


def _fmt(value: Decimal) -> str:
    return format(value.quantize(Decimal("0.01")), "f")


def validate_comparison_year(value: Any, *, current_year: int | None = None) -> str:
    text = str(value or DEFAULT_COMPARISON_YEAR).strip()
    if not text.isdigit() or len(text) > 4:
        raise HTTPException(status_code=422, detail="comparison year must be a past year")
    try:
        year = int(text)
    except ValueError:
        raise HTTPException(status_code=422, detail="comparison year must be a past year") from None
    ceiling = current_year or date.today().year
    if year < 1 or year >= ceiling:
        raise HTTPException(status_code=422, detail="comparison year must be a past year")
    return str(year)


def _normalise_amount(value: Any) -> str | None:
    if value is None or (isinstance(value, str) and not value.strip()):
        return None
    try:
        amount = Decimal(str(value).strip())
    except (InvalidOperation, ValueError):
        raise HTTPException(status_code=422, detail="invalid prior-year amount") from None
    if not amount.is_finite() or amount < 0 or amount > Decimal("999999999999.99"):
        raise HTTPException(status_code=422, detail="prior-year amount must be a non-negative CHF value")
    try:
        return _fmt(amount)
    except InvalidOperation:
        raise HTTPException(status_code=422, detail="invalid prior-year amount") from None


def _stored_amounts(conn: Connection, year: str) -> dict[str, str]:
    rows = conn.execute(
        """SELECT category_id, month, amount_text
           FROM budget_category_baselines
           WHERE year=? AND baseline_type='actual_previous_year'
           ORDER BY category_id, CASE WHEN month IS NULL THEN 0 ELSE 1 END, month""",
        (year,),
    ).fetchall()
    totals: dict[str, Decimal] = {}
    explicit_totals: set[str] = set()
    for row in rows:
        category_id = str(row["category_id"])
        amount = Decimal(str(row["amount_text"]))
        if row["month"] is None:
            totals[category_id] = amount
            explicit_totals.add(category_id)
        elif category_id not in explicit_totals:
            totals[category_id] = totals.get(category_id, Decimal("0")) + amount
    return {category_id: _fmt(amount) for category_id, amount in totals.items()}


def get_prior_year_actuals(conn: Connection, *, year: str = DEFAULT_COMPARISON_YEAR) -> dict[str, Any]:
    selected_year = validate_comparison_year(year)
    stored = _stored_amounts(conn, selected_year)
    categories = conn.execute(
        """SELECT category_id, name, sort_order
           FROM budget_categories
           WHERE is_active=1 AND category_type='expense'
           ORDER BY sort_order, name"""
    ).fetchall()
    rows: list[dict[str, Any]] = []
    recorded_total = Decimal("0")
    recorded_count = 0
    for category in categories:
        category_id = str(category["category_id"])
        amount = stored.get(category_id)
        if amount is not None:
            recorded_count += 1
            recorded_total += Decimal(amount)
        rows.append(
            {
                "category_id": category_id,
                "category": str(category["name"]),
                "annual_actual_chf": amount,
                "monthly_average_chf": _fmt(Decimal(amount) / Decimal("12")) if amount is not None else None,
                "recorded": amount is not None,
            }
        )
    available = {
        str(row["year"])
        for row in conn.execute(
            "SELECT DISTINCT year FROM budget_category_baselines WHERE baseline_type='actual_previous_year'"
        ).fetchall()
        if str(row["year"]).isdigit() and int(row["year"]) < date.today().year
    }
    available.add(DEFAULT_COMPARISON_YEAR)
    return {
        "purpose": "prior_year_category_actuals_v1",
        "year": selected_year,
        "default_year": DEFAULT_COMPARISON_YEAR,
        "available_years": sorted(available, key=int, reverse=True),
        "rows": rows,
        "totals": {
            "annual_actual_chf": _fmt(recorded_total) if recorded_count else None,
            "monthly_average_chf": _fmt(recorded_total / Decimal("12")) if recorded_count else None,
            "recorded_category_count": recorded_count,
            "missing_category_count": len(rows) - recorded_count,
        },
    }


def _source_version(conn: Connection, year: str) -> str:
    rows = conn.execute(
        """SELECT category_id, month, amount_text, updated_at
           FROM budget_category_baselines
           WHERE year=? AND baseline_type='actual_previous_year'
           ORDER BY category_id, month""",
        (year,),
    ).fetchall()
    payload = [list(row) for row in rows]
    return hashlib.sha256(json.dumps(payload, separators=(",", ":"), default=str).encode()).hexdigest()


def preview_prior_year_actuals(conn: Connection, payload: dict[str, Any]) -> dict[str, Any]:
    year = validate_comparison_year(payload.get("year"))
    raw_values = payload.get("values")
    if not isinstance(raw_values, list):
        raise HTTPException(status_code=422, detail="values must be a list")
    active = {
        str(row["category_id"])
        for row in conn.execute(
            "SELECT category_id FROM budget_categories WHERE is_active=1 AND category_type='expense'"
        ).fetchall()
    }
    seen: set[str] = set()
    values: list[dict[str, str | None]] = []
    for item in raw_values:
        if not isinstance(item, dict):
            raise HTTPException(status_code=422, detail="every prior-year value must be an object")
        category_id = str(item.get("category_id") or "")
        if category_id not in active:
            raise HTTPException(status_code=422, detail="prior-year values require an active expense category")
        if category_id in seen:
            raise HTTPException(status_code=422, detail="duplicate category in prior-year values")
        seen.add(category_id)
        values.append({"category_id": category_id, "amount_chf": _normalise_amount(item.get("amount_chf"))})
    values.sort(key=lambda item: str(item["category_id"]))
    source_version = _source_version(conn, year)
    binding = {"year": year, "values": values, "source_data_version": source_version}
    fingerprint = hashlib.sha256(json.dumps(binding, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
    recorded = [item for item in values if item["amount_chf"] is not None]
    total = sum((Decimal(str(item["amount_chf"])) for item in recorded), Decimal("0"))
    return {
        "preview_id": new_id("preview"),
        "preview_fingerprint": fingerprint,
        "source_data_version": source_version,
        "payload": {**binding, "preview_fingerprint": fingerprint},
        "review": {
            "recorded_category_count": len(recorded),
            "missing_category_count": len(values) - len(recorded),
            "annual_actual_total_chf": _fmt(total) if recorded else None,
            "monthly_average_total_chf": _fmt(total / Decimal("12")) if recorded else None,
        },
        "requires_explicit_confirm": True,
    }


def confirm_prior_year_actuals(conn: Connection, payload: dict[str, Any]) -> dict[str, Any]:
    supplied_fingerprint = str(payload.get("preview_fingerprint") or "")
    preview = preview_prior_year_actuals(conn, {"year": payload.get("year"), "values": payload.get("values")})
    if (
        payload.get("source_data_version") != preview["source_data_version"]
        or supplied_fingerprint != preview["preview_fingerprint"]
    ):
        raise HTTPException(status_code=409, detail="prior-year values changed; preview again")
    year = str(preview["payload"]["year"])
    old_values = _stored_amounts(conn, year)
    timestamp = now()
    for item in preview["payload"]["values"]:
        category_id = str(item["category_id"])
        conn.execute(
            "DELETE FROM budget_category_baselines WHERE category_id=? AND year=? AND baseline_type='actual_previous_year'",
            (category_id, year),
        )
        if item["amount_chf"] is not None:
            conn.execute(
                """INSERT INTO budget_category_baselines(
                       baseline_id, category_id, year, month, amount_text, currency,
                       baseline_type, source, notes, created_at, updated_at
                   ) VALUES (?, ?, ?, NULL, ?, 'CHF', 'actual_previous_year', 'manual', ?, ?, ?)""",
                (
                    new_id("bbase"),
                    category_id,
                    year,
                    item["amount_chf"],
                    "Manuell erfasster historischer Jahres-Ist-Wert",
                    timestamp,
                    timestamp,
                ),
            )
    new_values = _stored_amounts(conn, year)
    audit_id = record_audit_event(
        conn,
        source="vue_dashboard",
        action="prior_year_actuals_saved",
        entity_type="prior_year_actuals",
        entity_id=year,
        old_values={"year": year, "values": old_values},
        new_values={"year": year, "values": new_values, "changed_category_count": len(preview["payload"]["values"])},
        created_by="user",
    )
    conn.commit()
    return {
        "status": "confirmed",
        "entity_id": year,
        "audit_id": audit_id,
        "message": f"Vorjahreswerte {year} gespeichert",
        "saved_category_count": len(preview["payload"]["values"]),
    }
