from __future__ import annotations

from decimal import Decimal

from jarvis_finance.crypto.assets import create_crypto_asset
from jarvis_finance.crypto.holdings import create_initial_holding_snapshot
from jarvis_finance.crypto.wallets import create_wallet
from jarvis_finance.cli.main import main
from jarvis_finance.market.providers import PriceQuote, refresh_crypto_prices, store_crypto_price
from jarvis_finance.quality.alerts import create_alert
from jarvis_finance.quality.data_quality import check_crypto_data_quality
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations


class FreshProvider:
    def get_crypto_prices(self, coingecko_ids, currency="CHF"):
        return {
            cid: PriceQuote(
                coingecko_id=cid,
                currency=currency,
                price=Decimal("100"),
                provider="MockGecko",
                provider_timestamp="2026-01-31T00:00:00+00:00",
            )
            for cid in coingecko_ids
        }


class MissingProvider:
    def get_crypto_prices(self, coingecko_ids, currency="CHF"):
        return {
            cid: PriceQuote(
                coingecko_id=cid,
                currency=currency,
                price=None,
                quality_status="missing",
                error_message="not listed",
            )
            for cid in coingecko_ids
        }


def setup_crypto_conn():
    conn = connect_memory()
    apply_migrations(conn)
    wallet_id = create_wallet(
        conn,
        wallet_name="Synthetic Wallet",
        wallet_type="Hardware Wallet",
        platform_provider="Synthetic",
        last_verified_at="2026-01-31T00:00:00+00:00",
    )
    asset_id = create_crypto_asset(conn, coin_name="Bitcoin", symbol="BTC", coingecko_id="bitcoin")
    create_initial_holding_snapshot(
        conn,
        asset_id=asset_id,
        wallet_id=wallet_id,
        quantity=Decimal("0.01"),
        verification_status="verified",
        note="synthetic fixture",
        last_verified_at="2026-01-31T00:00:00+00:00",
    )
    return conn, asset_id


def test_stale_price_alert_is_resolved_when_price_is_fresh() -> None:
    conn, asset_id = setup_crypto_conn()
    alert_id = create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_stale",
        message="stale",
        evidence={"currency": "CHF"},
        fingerprint="crypto_price_stale:CHF",
    )
    store_crypto_price(conn, asset_id=asset_id, quote=PriceQuote("bitcoin", "CHF", Decimal("100")))

    result = check_crypto_data_quality(conn, currency="CHF", resolve_fixed=True)

    row = conn.execute("SELECT status, resolved_at, last_seen_at FROM alerts WHERE alert_id=?", (alert_id,)).fetchone()
    assert result.resolved_alerts == 1
    assert row["status"] == "resolved"
    assert row["resolved_at"]
    assert row["last_seen_at"]


def test_missing_price_alert_stays_active_when_price_is_still_missing() -> None:
    conn, asset_id = setup_crypto_conn()
    alert_id = create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_missing_local",
        message="missing",
        evidence={"currency": "CHF"},
        fingerprint="crypto_price_missing_local:CHF",
    )

    result = check_crypto_data_quality(conn, currency="CHF", resolve_fixed=True)

    row = conn.execute("SELECT status FROM alerts WHERE alert_id=?", (alert_id,)).fetchone()
    assert result.resolved_alerts == 0
    assert row["status"] == "active"


def test_resolved_alert_is_not_recreated_when_fingerprint_is_equal() -> None:
    conn, asset_id = setup_crypto_conn()
    create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_missing_local",
        message="missing",
        evidence={"currency": "CHF"},
        fingerprint="same-fingerprint",
    )
    store_crypto_price(conn, asset_id=asset_id, quote=PriceQuote("bitcoin", "CHF", Decimal("100")))
    check_crypto_data_quality(conn, currency="CHF", resolve_fixed=True)

    create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_missing_local",
        message="missing again",
        evidence={"currency": "CHF"},
        fingerprint="same-fingerprint",
    )

    rows = conn.execute("SELECT status, occurrence_count FROM alerts WHERE entity_id=? AND rule_id='crypto_price_missing_local'", (asset_id,)).fetchall()
    assert len(rows) == 1
    assert rows[0]["status"] == "resolved"
    assert rows[0]["occurrence_count"] == 2


def test_changed_error_fingerprint_creates_new_alert() -> None:
    conn, asset_id = setup_crypto_conn()
    create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_missing_local",
        message="missing one",
        evidence={"currency": "CHF", "error": "one"},
        fingerprint="error-one",
    )
    create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_missing_local",
        message="missing two",
        evidence={"currency": "CHF", "error": "two"},
        fingerprint="error-two",
    )

    count = conn.execute("SELECT COUNT(*) AS n FROM alerts WHERE entity_id=? AND rule_id='crypto_price_missing_local'", (asset_id,)).fetchone()["n"]
    assert count == 2


def test_refresh_auto_resolves_fixed_stale_alert_without_alert_flood() -> None:
    conn, asset_id = setup_crypto_conn()
    create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_stale",
        message="stale",
        evidence={"currency": "CHF"},
        fingerprint="crypto_price_stale:CHF",
    )

    refresh_crypto_prices(conn, provider=FreshProvider(), currency="CHF", max_age_seconds=3600, only_symbol="BTC")

    rows = conn.execute("SELECT status FROM alerts WHERE entity_id=? AND rule_id='crypto_price_stale'", (asset_id,)).fetchall()
    assert len(rows) == 1
    assert rows[0]["status"] == "resolved"


def test_cli_check_data_quality_resolves_fixed_alert(monkeypatch, tmp_path, capsys) -> None:
    runtime = tmp_path / "runtime"
    monkeypatch.setenv("JARVIS_FINANCE_RUNTIME_DIR", str(runtime))
    from jarvis_finance.storage.database import connect

    db = runtime / "data" / "finance.sqlite3"
    monkeypatch.setenv("JARVIS_FINANCE_DB_PATH", str(db))
    conn = connect(db)
    apply_migrations(conn)
    wallet_id = create_wallet(
        conn,
        wallet_name="CLI Wallet",
        wallet_type="Hardware Wallet",
        platform_provider="Synthetic",
        last_verified_at="2026-01-31T00:00:00+00:00",
    )
    asset_id = create_crypto_asset(conn, coin_name="Bitcoin", symbol="BTC", coingecko_id="bitcoin")
    create_initial_holding_snapshot(
        conn,
        asset_id=asset_id,
        wallet_id=wallet_id,
        quantity=Decimal("0.01"),
        verification_status="verified",
        note="synthetic fixture",
        last_verified_at="2026-01-31T00:00:00+00:00",
    )
    create_alert(
        conn,
        priority="warnung",
        category="market_data",
        entity_type="crypto_asset",
        entity_id=asset_id,
        rule_id="crypto_price_stale",
        message="stale",
        evidence={"currency": "CHF"},
        fingerprint="crypto_price_stale:CHF",
    )
    store_crypto_price(conn, asset_id=asset_id, quote=PriceQuote("bitcoin", "CHF", Decimal("100")))
    conn.close()

    rc = main(["check-data-quality", "--scope", "crypto", "--resolve-fixed"])

    out = capsys.readouterr().out
    assert rc == 0
    assert "DATA_QUALITY_CHECK" in out
    assert "resolved_alerts=1" in out


def test_failed_refresh_keeps_missing_alert_deduped() -> None:
    conn, asset_id = setup_crypto_conn()

    refresh_crypto_prices(conn, provider=MissingProvider(), currency="CHF", max_age_seconds=0, only_symbol="BTC")
    refresh_crypto_prices(conn, provider=MissingProvider(), currency="CHF", max_age_seconds=0, only_symbol="BTC")

    rows = conn.execute("SELECT status, occurrence_count FROM alerts WHERE entity_id=? AND rule_id='crypto_price_missing_local'", (asset_id,)).fetchall()
    assert len(rows) == 1
    assert rows[0]["status"] == "active"
    assert rows[0]["occurrence_count"] >= 2
