from __future__ import annotations

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

from .holdings import calculate_crypto_holdings


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


def _ds(value: Decimal | None) -> str | None:
    return format(value, "f") if value is not None else None

def _chf(value: Decimal, fx_rate_to_chf: Decimal | None) -> str | None:
    if fx_rate_to_chf is None:
        return None
    return _ds(value * fx_rate_to_chf)


def _ensure_positive(quantity: Decimal) -> None:
    if quantity <= 0:
        raise ValueError("quantity must be > 0")


def _current_quantity(conn: Connection, wallet_id: str, asset_id: str) -> Decimal:
    result = calculate_crypto_holdings(conn)
    holding = result.wallet_holdings.get((wallet_id, asset_id))
    return holding.quantity if holding else Decimal("0")


def _critical_negative_alert(conn: Connection, wallet_id: str, asset_id: str, attempted: Decimal, available: Decimal) -> None:
    create_alert(
        conn,
        priority="kritisch",
        category="crypto",
        entity_type="crypto_wallet",
        entity_id=wallet_id,
        rule_id="crypto_negative_wallet_balance",
        message="Crypto transaction would create a negative wallet balance and was blocked.",
        evidence={"asset_id": asset_id, "attempted_delta": str(attempted), "available": str(available)},
    )
    conn.commit()


def _insert_crypto_transaction(
    conn: Connection,
    *,
    tx_type: str,
    asset_id: str,
    quantity: Decimal,
    from_wallet_id: str | None = None,
    to_wallet_id: str | None = None,
    transaction_id: str | None = None,
    price_original: Decimal | None = None,
    currency: str | None = None,
    gross_amount_original: Decimal | None = None,
    fee_quantity: Decimal | None = None,
    fee_original: Decimal | None = None,
    fee_currency: str | None = None,
    fx_rate_to_chf: Decimal | None = None,
    tx_hash: str | None = None,
    note: str | None = None,
) -> str:
    now = utc_now()
    crypto_txid = stable_id("cryptotx", tx_type, asset_id, quantity, from_wallet_id or "", to_wallet_id or "", now)
    conn.execute(
        """
        INSERT INTO crypto_transactions(
            crypto_transaction_id, transaction_id, transaction_type, asset_id, quantity,
            price_original, currency_original, gross_amount_original, fee_quantity,
            fee_original, fee_currency, fx_rate_to_chf, amount_chf, from_wallet_id,
            to_wallet_id, transaction_datetime, tx_hash, source, confirmation_status,
            parse_confidence, notes, created_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'manual', 'confirmed', 1, ?, ?)
        """,
        (
            crypto_txid, transaction_id, tx_type, asset_id, _ds(quantity),
            _ds(price_original),
            currency, _ds(gross_amount_original),
            _ds(fee_quantity),
            _ds(fee_original),
            fee_currency, _ds(fx_rate_to_chf),
            _chf(gross_amount_original, fx_rate_to_chf) if gross_amount_original is not None else None,
            from_wallet_id, to_wallet_id, now, tx_hash, note, now,
        ),
    )
    return crypto_txid


def _insert_ledger_transaction(
    conn: Connection,
    *,
    tx_type: str,
    account_id: str,
    source_id: str,
    gross_amount_original: Decimal,
    currency: str,
    fx_rate_to_chf: Decimal | None,
    note: str,
    fee_original: Decimal = Decimal("0"),
) -> str:
    now = utc_now()
    txid = stable_id("ledger", "crypto", tx_type, account_id, source_id, gross_amount_original, fee_original)
    fx_status = "ok" if fx_rate_to_chf is not None else "missing"
    quality = "ok" if fx_rate_to_chf is not None else "incomplete"
    sign_net = gross_amount_original - fee_original if tx_type in {"partial_sell", "full_sell"} else gross_amount_original
    conn.execute(
        """
        INSERT INTO transactions(
            transaction_id, transaction_type, account_id, trade_date, gross_amount_original,
            fee_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status,
            gross_amount_chf, fee_chf, net_amount_chf, source_type, source_id,
            is_confirmed, quality_status, notes, created_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'crypto', ?, 1, ?, ?, ?)
        """,
        (
            txid, tx_type, account_id, now[:10], _ds(gross_amount_original), _ds(fee_original), _ds(sign_net),
            currency.upper(), _ds(fx_rate_to_chf), fx_status,
            _chf(gross_amount_original, fx_rate_to_chf), _chf(fee_original, fx_rate_to_chf), _chf(sign_net, fx_rate_to_chf),
            source_id, quality, note, now,
        ),
    )
    if fx_rate_to_chf is None and currency.upper() != "CHF":
        create_alert(
            conn,
            priority="kritisch",
            category="crypto",
            entity_type="transaction",
            entity_id=txid,
            rule_id="missing_fx",
            message="Crypto fiat ledger transaction is missing FX; CHF impact is incomplete.",
            evidence={"currency": currency.upper(), "source_id": source_id},
        )
    return txid


def _insert_fee_ledger(
    conn: Connection,
    *,
    account_id: str,
    source_id: str,
    fee_original: Decimal,
    fee_currency: str | None,
    fx_rate_to_chf: Decimal | None,
    note: str,
) -> str | None:
    if fee_original <= 0:
        return None
    if not fee_currency:
        create_alert(
            conn,
            priority="warnung",
            category="crypto",
            entity_type="crypto_transaction",
            entity_id=source_id,
            rule_id="crypto_fee_currency_unclear",
            message="Crypto fiat fee currency is unclear; ledger fee is not created to avoid double counting.",
            evidence={"fee_original": str(fee_original)},
        )
        return None
    return _insert_ledger_transaction(
        conn,
        tx_type="fee",
        account_id=account_id,
        source_id=source_id,
        gross_amount_original=fee_original,
        currency=fee_currency,
        fx_rate_to_chf=fx_rate_to_chf,
        note=note,
    )


def record_crypto_transfer(
    conn: Connection,
    *,
    asset_id: str,
    from_wallet_id: str,
    to_wallet_id: str,
    quantity: Decimal,
    fee_quantity: Decimal = Decimal("0"),
    tx_hash: str | None = None,
    note: str,
) -> str:
    _ensure_positive(quantity)
    if fee_quantity < 0:
        raise ValueError("fee_quantity must be >= 0")
    if from_wallet_id == to_wallet_id:
        raise ValueError("source and target wallets must be different")
    required = quantity + fee_quantity
    available = _current_quantity(conn, from_wallet_id, asset_id)
    if available < required:
        _critical_negative_alert(conn, from_wallet_id, asset_id, required, available)
        raise ValueError("negative wallet balance blocked")
    crypto_txid = _insert_crypto_transaction(
        conn,
        tx_type="transfer",
        asset_id=asset_id,
        quantity=quantity,
        fee_quantity=fee_quantity,
        from_wallet_id=from_wallet_id,
        to_wallet_id=to_wallet_id,
        tx_hash=tx_hash,
        note=note,
    )
    record_audit_event(
        conn,
        source="crypto_transactions",
        action="crypto_transfer",
        entity_type="crypto_transaction",
        entity_id=crypto_txid,
        new_values={"asset_id": asset_id, "from_wallet_id": from_wallet_id, "to_wallet_id": to_wallet_id, "quantity": _ds(quantity), "fee_quantity": str(fee_quantity)},
        user_text_note=note,
        confirmed=True,
        created_by="system",
    )
    conn.commit()
    return crypto_txid


def record_crypto_buy(
    conn: Connection,
    *,
    account_id: str,
    asset_id: str,
    to_wallet_id: str,
    quantity: Decimal,
    gross_amount_original: Decimal,
    currency: str,
    note: str,
    fee_original: Decimal = Decimal("0"),
    fee_currency: str | None = None,
    fee_quantity: Decimal = Decimal("0"),
    fx_rate_to_chf: Decimal | None = None,
) -> str:
    _ensure_positive(quantity)
    if fee_quantity < 0 or fee_original < 0:
        raise ValueError("fees must be >= 0")
    # Placeholder ID first so ledger can refer to the crypto event; then update link.
    provisional = stable_id("cryptobuy", asset_id, to_wallet_id, quantity, utc_now())
    ledger_txid = _insert_ledger_transaction(
        conn,
        tx_type="buy",
        account_id=account_id,
        source_id=provisional,
        gross_amount_original=gross_amount_original,
        currency=currency,
        fx_rate_to_chf=fx_rate_to_chf,
        note=note,
    )
    crypto_txid = _insert_crypto_transaction(
        conn,
        tx_type="buy",
        asset_id=asset_id,
        quantity=quantity,
        to_wallet_id=to_wallet_id,
        transaction_id=ledger_txid,
        gross_amount_original=gross_amount_original,
        fee_quantity=fee_quantity,
        fee_original=fee_original,
        fee_currency=fee_currency,
        currency=currency.upper(),
        fx_rate_to_chf=fx_rate_to_chf,
        note=note,
    )
    conn.execute("UPDATE transactions SET source_id=? WHERE transaction_id=?", (crypto_txid, ledger_txid))
    _insert_fee_ledger(conn, account_id=account_id, source_id=crypto_txid, fee_original=fee_original, fee_currency=fee_currency, fx_rate_to_chf=fx_rate_to_chf, note=note)
    record_audit_event(
        conn,
        source="crypto_transactions",
        action="crypto_buy",
        entity_type="crypto_transaction",
        entity_id=crypto_txid,
        new_values={"asset_id": asset_id, "to_wallet_id": to_wallet_id, "quantity": _ds(quantity), "ledger_transaction_id": ledger_txid},
        user_text_note=note,
        confirmed=True,
        created_by="system",
    )
    conn.commit()
    return crypto_txid


def record_crypto_sell(
    conn: Connection,
    *,
    account_id: str,
    asset_id: str,
    from_wallet_id: str,
    quantity: Decimal,
    gross_amount_original: Decimal,
    currency: str,
    note: str,
    fee_original: Decimal = Decimal("0"),
    fee_currency: str | None = None,
    fee_quantity: Decimal = Decimal("0"),
    fx_rate_to_chf: Decimal | None = None,
) -> str:
    _ensure_positive(quantity)
    if fee_quantity < 0 or fee_original < 0:
        raise ValueError("fees must be >= 0")
    required = quantity + fee_quantity
    available = _current_quantity(conn, from_wallet_id, asset_id)
    if available < required:
        _critical_negative_alert(conn, from_wallet_id, asset_id, required, available)
        raise ValueError("negative wallet balance blocked")
    provisional = stable_id("cryptosell", asset_id, from_wallet_id, quantity, utc_now())
    ledger_txid = _insert_ledger_transaction(
        conn,
        tx_type="partial_sell",
        account_id=account_id,
        source_id=provisional,
        gross_amount_original=gross_amount_original,
        fee_original=fee_original,
        currency=currency,
        fx_rate_to_chf=fx_rate_to_chf,
        note=note,
    )
    crypto_txid = _insert_crypto_transaction(
        conn,
        tx_type="sell",
        asset_id=asset_id,
        quantity=quantity,
        from_wallet_id=from_wallet_id,
        transaction_id=ledger_txid,
        gross_amount_original=gross_amount_original,
        fee_quantity=fee_quantity,
        fee_original=fee_original,
        fee_currency=fee_currency,
        currency=currency.upper(),
        fx_rate_to_chf=fx_rate_to_chf,
        note=note,
    )
    conn.execute("UPDATE transactions SET source_id=? WHERE transaction_id=?", (crypto_txid, ledger_txid))
    # Sell fee is represented in the sell net amount; do not create a separate fee row.
    if fee_original > 0 and not fee_currency:
        create_alert(
            conn,
            priority="warnung",
            category="crypto",
            entity_type="crypto_transaction",
            entity_id=crypto_txid,
            rule_id="crypto_fee_currency_unclear",
            message="Crypto sell fee currency is unclear; fee remains embedded in sell transaction.",
            evidence={"fee_original": str(fee_original)},
        )
    record_audit_event(
        conn,
        source="crypto_transactions",
        action="crypto_sell",
        entity_type="crypto_transaction",
        entity_id=crypto_txid,
        new_values={"asset_id": asset_id, "from_wallet_id": from_wallet_id, "quantity": _ds(quantity), "ledger_transaction_id": ledger_txid},
        user_text_note=note,
        confirmed=True,
        created_by="system",
    )
    conn.commit()
    return crypto_txid
