from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal
from sqlite3 import Connection

from jarvis_finance.audit.log import record_audit_event
from jarvis_finance.imports.common import stable_id, utc_now
from jarvis_finance.quality.alerts import create_alert


@dataclass
class CashBalance:
    account_id: str
    currency: str
    amount_original: Decimal = Decimal("0")
    amount_chf: Decimal | None = Decimal("0")
    quality_status: str = "ok"


@dataclass
class CashCalculationResult:
    balances: dict[tuple[str, str], CashBalance] = field(default_factory=dict)
    negative_cash_warnings: list[str] = field(default_factory=list)


def _d(value: object, default: str = "0") -> Decimal:
    if value is None or str(value).strip() == "":
        return Decimal(default)
    return Decimal(str(value))


def _signed_cash_delta(row) -> tuple[Decimal, Decimal | None]:
    t = row["transaction_type"]
    gross = _d(row["gross_amount_original"])
    net = _d(row["net_amount_original"]) if row["net_amount_original"] is not None else None
    fee = _d(row["fee_original"])
    tax = _d(row["tax_original"])
    fx = _d(row["fx_rate_to_chf"], "1") if row["fx_rate_to_chf"] is not None else None

    if t == "initial_cash_snapshot":
        original = net if net is not None else gross
    elif t == "cash_deposit":
        original = net if net is not None else gross
    elif t == "cash_withdrawal":
        original = -(net if net is not None else gross)
    elif t == "buy":
        original = -(net if net is not None else gross + fee + tax)
    elif t in {"partial_sell", "full_sell"}:
        original = net if net is not None else gross - fee - tax
    elif t in {"dividend", "etf_distribution"}:
        original = net if net is not None else gross - tax - fee
    elif t in {"fee", "tax"}:
        original = -(net if net is not None else gross or fee or tax)
    elif t == "fx_conversion":
        original = net if net is not None else gross
    elif t == "manual_cash_correction":
        original = net if net is not None else gross
    else:
        original = Decimal("0")
    chf = original * fx if fx is not None else None
    return original, chf


def calculate_cash_balances(conn: Connection, *, as_of_date: str | None = None) -> CashCalculationResult:
    result = CashCalculationResult()
    params: tuple[object, ...] = ()
    where = "WHERE COALESCE(is_voided, 0) = 0"
    if as_of_date:
        where += " AND trade_date <= ?"
        params = (as_of_date,)
    rows = conn.execute(
        f"""
        SELECT * FROM transactions
        {where}
        ORDER BY account_id, currency_original, trade_date, created_at, transaction_id
        """,
        params,
    ).fetchall()
    for row in rows:
        account_id = row["account_id"]
        currency = row["currency_original"]
        key = (account_id, currency)
        balance = result.balances.setdefault(key, CashBalance(account_id, currency))
        original, chf = _signed_cash_delta(row)
        balance.amount_original += original
        if chf is None:
            balance.amount_chf = None
            balance.quality_status = "incomplete"
        elif balance.amount_chf is not None:
            balance.amount_chf += chf
    for key, balance in result.balances.items():
        if balance.amount_original < 0:
            msg = f"Negative cash balance for account={balance.account_id} currency={balance.currency}: {balance.amount_original}"
            result.negative_cash_warnings.append(msg)
            create_alert(
                conn,
                priority="warnung",
                category="cash",
                entity_type="account",
                entity_id=balance.account_id,
                rule_id="negative_cash",
                message=msg,
                evidence={"currency": balance.currency, "amount_original": str(balance.amount_original)},
            )
    conn.commit()
    return result


def apply_manual_cash_correction(
    conn: Connection,
    *,
    account_id: str,
    currency: str,
    amount: Decimal,
    note: str,
    trade_date: str | None = None,
) -> str:
    if not note.strip():
        raise ValueError("manual cash correction requires a note")
    now = utc_now()
    trade_date = trade_date or now[:10]
    transaction_id = stable_id("cashcorr", account_id, currency, trade_date, amount, note)
    fx_rate = Decimal("1") if currency.upper() == "CHF" else None
    conn.execute(
        """
        INSERT INTO transactions(
            transaction_id, transaction_type, account_id, trade_date, gross_amount_original,
            net_amount_original, currency_original, fx_rate_to_chf, fx_status,
            gross_amount_chf, net_amount_chf, source_type, is_confirmed,
            quality_status, notes, created_at
        ) VALUES (?, 'manual_cash_correction', ?, ?, ?, ?, ?, ?, ?, ?, ?, 'manual', 1, ?, ?, ?)
        """,
        (
            transaction_id, account_id, trade_date, str(amount), str(amount), currency.upper(),
            str(fx_rate) if fx_rate is not None else None,
            "ok" if fx_rate is not None else "missing",
            str(amount * fx_rate) if fx_rate is not None else None,
            str(amount * fx_rate) if fx_rate is not None else None,
            "ok" if fx_rate is not None else "incomplete",
            note,
            now,
        ),
    )
    audit_id = record_audit_event(
        conn,
        source="manual_cash_correction",
        action="manual_cash_correction",
        entity_type="transaction",
        entity_id=transaction_id,
        new_values={"account_id": account_id, "currency": currency.upper(), "amount": str(amount)},
        user_text_note=note,
        confirmed=True,
        created_by="system",
    )
    conn.commit()
    return audit_id
