from __future__ import annotations

from pathlib import Path

from fastapi.testclient import TestClient

from jarvis_finance.api.main import create_app
from jarvis_finance.api.schemas.crypto_reconciliation import (
    CryptoSnapshotConfirmRequest,
    CryptoSnapshotPreviewRequest,
    CryptoTransferPairConfirmRequest,
    CryptoTransferPairPreviewRequest,
)
from jarvis_finance.crypto.current_balances import current_crypto_balance_basis
from jarvis_finance.services.crypto_market_recovery import _current_inventory
from jarvis_finance.services.crypto_reconciliation import (
    confirm_crypto_snapshot,
    confirm_crypto_transfer_pair,
    get_crypto_reconciliation,
    preview_crypto_snapshot,
    preview_crypto_transfer_pair,
)
from jarvis_finance.storage.database import connect
from jarvis_finance.storage.migrations import apply_migrations, get_schema_version

NOW = "2026-08-09T12:00:00Z"


def _db(tmp_path: Path):
    conn = connect(tmp_path / "crypto-reconciliation.sqlite3")
    apply_migrations(conn)
    conn.execute("INSERT INTO crypto_wallets(wallet_id,wallet_name,wallet_type,is_active,created_at) VALUES ('w1','Exchange A','exchange',1,?)", (NOW,))
    conn.execute("INSERT INTO crypto_wallets(wallet_id,wallet_name,wallet_type,is_active,created_at) VALUES ('w2','Cold Wallet','self_custody',1,?)", (NOW,))
    conn.execute("INSERT INTO crypto_assets(asset_id,coin_name,symbol,coingecko_id,is_active,created_at) VALUES ('btc','Bitcoin','BTC','bitcoin',1,?)", (NOW,))
    conn.execute("""INSERT INTO crypto_holdings(crypto_holding_id,asset_id,wallet_id,quantity,last_verified_at,verification_status,legacy_snapshot_date,created_at)
                    VALUES ('h1','btc','w1','1','2025-12-31','verified','2025-12-31',?)""", (NOW,))
    conn.execute("INSERT INTO crypto_prices(crypto_price_id,asset_id,price_currency,price,provider,provider_timestamp,fetched_at,quality_status) VALUES ('p1','btc','CHF','100000','CoinGecko',?,?, 'fresh')", (NOW, NOW))
    conn.commit()
    return conn


def _payload():
    return {
        "observed_at": "2026-08-09T14:00:00+02:00",
        "wallets": [
            {"wallet_id": "w1", "evidence_source": "Exchange display", "note": "address 0x1234567890abcdef1234567890abcdef12345678", "items": [{"asset_id": "btc", "quantity": "1.25"}]},
            {"wallet_id": "w2", "evidence_source": "Wallet app", "note": "", "items": []},
        ],
    }


def test_schema_50_and_snapshot_preview_confirm_replay_are_append_only(tmp_path: Path) -> None:
    conn = _db(tmp_path)
    assert get_schema_version(conn) == 53
    request = CryptoSnapshotPreviewRequest(**_payload())
    before = {table: conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0] for table in ("crypto_balance_snapshots", "crypto_balance_snapshot_items", "audit_log")}
    preview = preview_crypto_snapshot(conn, request)
    assert preview["status"] == "complete"
    assert preview["differences"][0]["unexplained_difference"] == "0.25"
    assert preview["differences"][0]["status"] == "unexplained_balance_change"
    assert before == {table: conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0] for table in before}

    confirm_request = CryptoSnapshotConfirmRequest(**_payload(), preview_id=preview["preview_id"], confirmation_key="snapshot-2026-08-09", confirm=True)
    first = confirm_crypto_snapshot(conn, confirm_request)
    second = confirm_crypto_snapshot(conn, confirm_request)
    assert first["status"] == "confirmed"
    assert second["status"] == "already_confirmed"
    assert second["idempotent_replay"] is True
    assert conn.execute("SELECT COUNT(*) FROM crypto_balance_snapshots").fetchone()[0] == 1
    assert conn.execute("SELECT COUNT(*) FROM crypto_balance_snapshot_items").fetchone()[0] == 1
    assert conn.execute("SELECT quantity FROM crypto_holdings WHERE crypto_holding_id='h1'").fetchone()[0] == "1"
    assert "[REDACTED]" in conn.execute("SELECT redacted_note FROM crypto_balance_snapshot_wallets WHERE wallet_id='w1'").fetchone()[0]
    basis = current_crypto_balance_basis(conn)
    assert basis.confirmed_current is True
    assert str(basis.quantities[("w1", "btc")]) == "1.25"
    inventory, inventory_reasons = _current_inventory(conn)
    assert inventory[0]["quantity"] == "1.25"
    assert inventory_reasons == []

    cockpit = get_crypto_reconciliation(conn)
    assert cockpit["valuation"]["value_chf"] == "125000.00"
    assert cockpit["valuation"]["balance_as_of"] == "2026-08-09T12:00:00Z"
    assert cockpit["performance_gate"]["status"] == "closed"
    assert cockpit["render_provider_calls"] is False
    assert cockpit["differences"][0]["status"] == "unexplained_balance_change"


def test_partial_snapshot_does_not_replace_current_balance_basis(tmp_path: Path) -> None:
    conn = _db(tmp_path)
    payload = _payload()
    payload["wallets"] = payload["wallets"][:1]
    preview = preview_crypto_snapshot(conn, CryptoSnapshotPreviewRequest(**payload))
    assert preview["status"] == "partial"
    result = confirm_crypto_snapshot(conn, CryptoSnapshotConfirmRequest(**payload, preview_id=preview["preview_id"], confirmation_key="partial", confirm=True))
    assert result["snapshot_status"] == "partial"
    basis = current_crypto_balance_basis(conn)
    assert basis.confirmed_current is False
    assert str(basis.quantities[("w1", "btc")]) == "1"


def test_manual_transfer_pair_keeps_two_legs_and_is_idempotent(tmp_path: Path) -> None:
    conn = _db(tmp_path)
    conn.execute("""INSERT INTO crypto_transactions(crypto_transaction_id,transaction_type,asset_id,quantity,fee_quantity,from_wallet_id,transaction_datetime,tx_hash,source,confirmation_status,created_at)
                    VALUES ('out','withdrawal','btc','0.5','0.01','w1',?,'hash-1','export','confirmed',?)""", (NOW, NOW))
    conn.execute("""INSERT INTO crypto_transactions(crypto_transaction_id,transaction_type,asset_id,quantity,to_wallet_id,transaction_datetime,tx_hash,source,confirmation_status,created_at)
                    VALUES ('in','deposit','btc','0.49','w2',?,'hash-1','export','confirmed',?)""", (NOW, NOW))
    conn.commit()
    request = CryptoTransferPairPreviewRequest(withdrawal_transaction_id="out", deposit_transaction_id="in", evidence_reference="hash-1", note="confirmed in both apps")
    preview = preview_crypto_transfer_pair(conn, request)
    confirm_request = CryptoTransferPairConfirmRequest(**request.model_dump(), preview_id=preview["preview_id"], confirmation_key="pair-1", confirm=True)
    first = confirm_crypto_transfer_pair(conn, confirm_request)
    second = confirm_crypto_transfer_pair(conn, confirm_request)
    assert first["status"] == "confirmed"
    assert second["idempotent_replay"] is True
    assert conn.execute("SELECT COUNT(*) FROM crypto_transactions").fetchone()[0] == 2
    assert conn.execute("SELECT COUNT(*) FROM crypto_internal_transfer_pairs").fetchone()[0] == 1
    assert preview["external_cashflow_effect"] == "0"


def test_reconciliation_api_render_is_read_only_and_exposes_dates(tmp_path: Path, monkeypatch) -> None:
    conn = _db(tmp_path)
    import jarvis_finance.api.dependencies as dependencies

    monkeypatch.setattr(dependencies, "connect", lambda *_args, **_kwargs: conn)
    client = TestClient(create_app(write_mode="test"))
    response = client.get("/api/crypto/reconciliation")
    assert response.status_code == 200
    body = response.json()
    assert body["anchor_date"] == "2025-12-31"
    assert body["valuation"]["price_as_of"] == NOW
    assert "aktueller Portfoliowert nicht bestätigt" in body["valuation"]["status_message"]
    assert body["render_provider_calls"] is False
