from __future__ import annotations

from collections.abc import Iterator
import sqlite3
from sqlite3 import Connection

from fastapi.testclient import TestClient

from jarvis_finance.api.dependencies import get_db
from jarvis_finance.api.main import create_app
from jarvis_finance.storage.migrations import apply_migrations


def _connect() -> Connection:
    conn = sqlite3.connect(":memory:", check_same_thread=False)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA foreign_keys = ON")
    return conn


def _client(conn: Connection) -> TestClient:
    app = create_app()

    def override_db() -> Iterator[Connection]:
        yield conn

    app.dependency_overrides[get_db] = override_db
    return TestClient(app)


def _seed(conn: Connection) -> None:
    now = "2026-05-16T12:00:00Z"
    conn.execute("INSERT INTO platforms(platform_id, name, platform_type, country, default_currency, created_at) VALUES ('p1', 'Raiffeisen', 'bank', 'CH', 'CHF', ?)", (now,))
    conn.execute("INSERT INTO platforms(platform_id, name, platform_type, country, default_currency, created_at) VALUES ('p2', 'True Wealth', 'broker', 'CH', 'CHF', ?)", (now,))
    conn.execute("INSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, created_at) VALUES ('cash1', 'p1', 'Privatkonto', 'cash', 'CHF', ?)", (now,))
    conn.execute("INSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, created_at) VALUES ('broker1', 'p2', 'Depot', 'brokerage', 'CHF', ?)", (now,))
    conn.execute("INSERT INTO instruments(instrument_id, asset_class, name, ticker, isin, currency, created_at) VALUES ('inst1', 'stock', 'Demo Equity', 'DEMO', 'CH0000000000', 'CHF', ?)", (now,))
    conn.execute("INSERT INTO fx_rates(fx_rate_id, base_currency, quote_currency, rate_date, rate, provider, rate_type, quality_status, created_at) VALUES ('fx_usd', 'USD', 'CHF', '2026-05-17', '0.91', 'fixture', 'close', 'fresh', ?)", (now,))
    conn.execute("INSERT INTO market_prices(market_price_id, instrument_id, price_date, close, currency, provider, quality_status, created_at) VALUES ('mp1', 'inst1', '2026-05-16', '12', 'CHF', 'local', 'ok', ?)", (now,))
    conn.execute("INSERT INTO transactions(transaction_id, transaction_type, account_id, instrument_id, trade_date, quantity, 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 ('buy1', 'buy', 'broker1', 'inst1', '2026-05-16', '10', '100', '100', 'CHF', '1', 'not_needed', '100', '100', 'fixture', 1, 'ok', 'opening buy', ?)", (now,))
    conn.execute("INSERT INTO cash_balances(cash_balance_id, account_id, balance_date, currency, amount_original, fx_rate_to_chf, amount_chf, source_type, quality_status, created_at) VALUES ('cb1', 'cash1', '2026-05-16', 'CHF', '1000', '1', '1000', 'fixture', 'ok', ?)", (now,))
    conn.execute("INSERT INTO crypto_wallets(wallet_id, wallet_name, wallet_type, created_at) VALUES ('w1', 'Cold Wallet', 'hardware', ?)", (now,))
    conn.execute("INSERT INTO crypto_wallets(wallet_id, wallet_name, wallet_type, created_at) VALUES ('w2', 'Exchange Wallet', 'exchange', ?)", (now,))
    conn.execute("INSERT INTO crypto_assets(asset_id, symbol, coin_name, coingecko_id, created_at) VALUES ('btc', 'BTC', 'Bitcoin', 'bitcoin', ?)", (now,))
    conn.execute("INSERT INTO crypto_holdings(crypto_holding_id, wallet_id, asset_id, quantity, verification_status, created_at) VALUES ('h1', 'w1', 'btc', '0.25000000', 'ok', ?)", (now,))
    conn.commit()


def test_crypto_transfer_moves_between_wallets_preserves_total_minus_fee_and_audits() -> None:
    conn = _connect(); apply_migrations(conn); _seed(conn)
    client = _client(conn)
    request = {
        "asset_id": "btc",
        "wallet_id": "w1",
        "target_wallet_id": "w2",
        "quantity": "0.10000000",
        "fee_quantity": "0.00010000",
        "transaction_type": "transfer",
        "effective_date": "2026-05-17",
        "note": "move to exchange",
    }

    preview = client.post("/api/crypto/actions/preview", json=request).json()
    assert preview["summary"].startswith("Crypto Transfer")
    assert conn.execute("SELECT quantity FROM crypto_holdings WHERE wallet_id='w1' AND asset_id='btc'").fetchone()[0] == "0.25000000"

    confirmed = client.post("/api/crypto/actions/confirm", json={**request, "preview_id": preview["preview_id"], "confirm": True}).json()

    assert confirmed["status"] == "confirmed"
    holdings = {row["wallet_id"]: row["quantity"] for row in conn.execute("SELECT wallet_id, quantity FROM crypto_holdings WHERE asset_id='btc'").fetchall()}
    assert holdings["w1"] == "0.14990000"
    assert holdings["w2"] == "0.10000000"
    transfer_row = conn.execute("SELECT from_wallet_id, to_wallet_id, quantity, fee_quantity FROM crypto_transactions WHERE transaction_type='transfer'").fetchone()
    assert transfer_row[:] == ("w1", "w2", "0.10000000", "0.00010000")
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action='crypto_transfer_confirm'").fetchone()[0] == 1


def test_crypto_transfer_blocks_negative_or_oversized_quantity() -> None:
    conn = _connect(); apply_migrations(conn); _seed(conn)
    client = _client(conn)

    negative = client.post("/api/crypto/actions/preview", json={"asset_id": "btc", "wallet_id": "w1", "target_wallet_id": "w2", "quantity": "-0.1", "transaction_type": "transfer", "effective_date": "2026-05-17", "note": "bad"})
    assert negative.status_code == 422

    oversized = client.post("/api/crypto/actions/confirm", json={"asset_id": "btc", "wallet_id": "w1", "target_wallet_id": "w2", "quantity": "0.3", "fee_quantity": "0", "transaction_type": "transfer", "effective_date": "2026-05-17", "note": "too much", "preview_id": "preview_test", "confirm": True})
    assert oversized.status_code == 422


def test_equity_sale_partial_full_and_oversell_rules_with_audit_and_cash() -> None:
    conn = _connect(); apply_migrations(conn); _seed(conn)
    client = _client(conn)
    sale = {"position_id": "broker1:inst1", "cash_account_id": "cash1", "quantity": "4", "price_original": "12", "currency": "CHF", "trade_date": "2026-05-17", "fees_original": "1", "note": "partial sale"}

    preview = client.post("/api/equity/actions/sell/preview", json=sale).json()
    assert preview["summary"].startswith("Verkauf")
    assert preview["amount_chf"] == "47.00"

    confirmed = client.post("/api/equity/actions/sell/confirm", json={**sale, "preview_id": preview["preview_id"], "confirm": True}).json()
    assert confirmed["status"] == "confirmed"
    tx = conn.execute("SELECT transaction_type, quantity, net_amount_original, fee_original FROM transactions WHERE notes='partial sale'").fetchone()
    assert tx[:] == ("sell", "-4", "47", "1")
    assert conn.execute("SELECT COUNT(*) FROM cash_balances WHERE source_type LIKE 'vue_equity_sale:%'").fetchone()[0] == 1
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action='equity_sell_confirm'").fetchone()[0] == 1

    full = {**sale, "quantity": "6", "note": "full sale"}
    full_preview = client.post("/api/equity/actions/sell/preview", json=full).json()
    full_confirmed = client.post("/api/equity/actions/sell/confirm", json={**full, "preview_id": full_preview["preview_id"], "confirm": True}).json()
    assert full_confirmed["status"] == "confirmed"

    blocked = client.post("/api/equity/actions/sell/preview", json={**sale, "quantity": "0.1", "note": "oversell"})
    assert blocked.status_code == 422


def test_equity_dividend_writes_income_cash_net_tax_fields_and_audit_without_quantity_change() -> None:
    conn = _connect(); apply_migrations(conn); _seed(conn)
    client = _client(conn)
    dividend = {"position_id": "broker1:inst1", "cash_account_id": "cash1", "gross_amount": "100", "currency": "CHF", "swiss_tax": "35", "foreign_tax": "5", "other_deductions": "0", "payment_date": "2026-05-17", "note": "quarterly dividend"}

    preview = client.post("/api/equity/actions/dividend/preview", json=dividend).json()
    assert preview["summary"].startswith("Dividende")
    assert preview["amount_chf"] == "60.00"

    confirmed = client.post("/api/equity/actions/dividend/confirm", json={**dividend, "preview_id": preview["preview_id"], "confirm": True}).json()

    assert confirmed["status"] == "confirmed"
    row = conn.execute("SELECT transaction_type, quantity, gross_amount_original, tax_original, net_amount_original FROM transactions WHERE notes='quarterly dividend'").fetchone()
    assert row[:] == ("dividend", None, "100", "40", "60")
    assert conn.execute("SELECT COUNT(*) FROM cash_balances WHERE source_type='vue_equity_dividend'").fetchone()[0] == 1
    assert conn.execute("SELECT SUM(CAST(quantity AS REAL)) FROM transactions WHERE account_id='broker1' AND instrument_id='inst1' AND coalesce(is_voided,0)=0").fetchone()[0] == 10
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action='equity_dividend_confirm'").fetchone()[0] == 1


def test_equity_inline_buy_reuses_position_confirm_with_prefilled_instrument_and_audit() -> None:
    conn = _connect(); apply_migrations(conn); _seed(conn)
    client = _client(conn)
    buy = {"asset_class": "stock", "account_id": "broker1", "instrument_id": "inst1", "quantity": "2", "currency": "CHF", "trade_date": "2026-05-17", "transaction_type": "buy", "cost_basis_original": "24", "note": "inline buy"}

    preview = client.post("/api/positions/preview", json=buy).json()
    confirmed = client.post("/api/positions/confirm", json={**buy, "preview_id": preview["preview_id"], "confirm": True}).json()

    assert confirmed["status"] == "confirmed"
    assert conn.execute("SELECT transaction_type FROM transactions WHERE notes='inline buy'").fetchone()[0] == "buy"
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action='position_confirm'").fetchone()[0] == 1


def test_inline_account_and_wallet_creation_refresh_targets_and_audit() -> None:
    conn = _connect(); apply_migrations(conn); _seed(conn)
    client = _client(conn)

    account_preview = client.post("/api/accounts/preview", json={"platform_name": "Inline Bank", "account_name": "Inline Cash", "account_type": "cash", "currency": "CHF", "note": "inline account"}).json()
    account = client.post("/api/accounts/confirm", json={"platform_name": "Inline Bank", "account_name": "Inline Cash", "account_type": "cash", "currency": "CHF", "note": "inline account", "preview_id": account_preview["preview_id"], "confirm": True}).json()
    assert account["entity_id"] in [row["account_id"] for row in client.get("/api/reference/accounts").json()]

    wallet_preview = client.post("/api/wallets/preview", json={"name": "Inline Wallet", "wallet_type": "software", "platform_name": "MetaMask", "note": "inline wallet"}).json()
    wallet = client.post("/api/wallets/confirm", json={"name": "Inline Wallet", "wallet_type": "software", "platform_name": "MetaMask", "note": "inline wallet", "preview_id": wallet_preview["preview_id"], "confirm": True}).json()
    assert wallet["entity_id"] in [row["wallet_id"] for row in client.get("/api/wallets").json()]
    assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action IN ('account_create_confirm','wallet_create_confirm')").fetchone()[0] == 2
