from __future__ import annotations

import json
from datetime import datetime, timezone

import polars as pl
from fastapi.testclient import TestClient

from uforia.api.app import create_app
from uforia.api.settings import ApiSettings
from uforia.config import get_config
from uforia.execution.service import ExecutionService
from uforia.storage import DatasetName, DatasetStore


def _write_account_config(tmp_path) -> None:
    config_dir = tmp_path / "config"
    config_dir.mkdir(parents=True, exist_ok=True)
    (config_dir / "execution_accounts.json").write_text(
        json.dumps(
            [
                {
                    "label": "main",
                    "venue": "hyperliquid",
                    "wallet_address": "0xabc",
                    "enabled_assets": ["BTC", "ETH"],
                    "enabled_symbols": ["BTC-PERP"],
                    "max_order_notional_usd": 100000,
                    "max_position_notional_usd": 250000,
                    "enabled": True,
                    "kill_switch": False,
                }
            ]
        )
    )


def test_execution_api_lists_venues_and_health(tmp_path, monkeypatch) -> None:
    _write_account_config(tmp_path)
    monkeypatch.setenv("UFORIA_EXECUTION_ENABLED", "true")
    monkeypatch.setenv(
        "UFORIA_EXECUTION_ACCOUNTS_PATH",
        str(tmp_path / "config" / "execution_accounts.json"),
    )
    monkeypatch.setenv("UFORIA_EXECUTION_KILL_SWITCH", "false")
    monkeypatch.setenv("UFORIA_EXECUTION_MAX_ORDER_NOTIONAL_USD", "200000")

    store = DatasetStore(tmp_path)
    ts = datetime(2026, 3, 12, 10, tzinfo=timezone.utc)
    store.write(
        DatasetName.EXECUTION_HEALTH_SNAPSHOT,
        pl.DataFrame(
            [
                {
                    "ts": ts,
                    "date": ts.date(),
                    "venue": "hyperliquid",
                    "account": "main",
                    "status": "online",
                    "ws_connected": True,
                    "private_ws_connected": False,
                    "last_heartbeat_ms": 250,
                    "latency_ms": 42,
                    "last_error": None,
                    "reconnect_count": 0,
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    venues = client.get("/api/v1/execution/venues")
    accounts = client.get("/api/v1/execution/accounts")
    health = client.get("/api/v1/execution/health")

    assert venues.status_code == 200
    assert any(row["venue"] == "hyperliquid" for row in venues.json()["data"])
    assert accounts.status_code == 200
    assert accounts.json()["data"][0]["label"] == "main"
    assert health.status_code == 200
    assert health.json()["data"][0]["status"] == "online"


def test_execution_order_dry_run_writes_audit_rows(tmp_path, monkeypatch) -> None:
    _write_account_config(tmp_path)
    monkeypatch.setenv("UFORIA_EXECUTION_ENABLED", "true")
    monkeypatch.setenv(
        "UFORIA_EXECUTION_ACCOUNTS_PATH",
        str(tmp_path / "config" / "execution_accounts.json"),
    )
    monkeypatch.setenv("UFORIA_EXECUTION_KILL_SWITCH", "false")

    store = DatasetStore(tmp_path)
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.post(
        "/api/v1/execution/order",
        json={
            "venue": "hyperliquid",
            "account": "main",
            "instrument_type": "perp",
            "asset": "BTC",
            "symbol": "BTC-PERP",
            "side": "buy",
            "order_type": "limit",
            "time_in_force": "gtc",
            "price": 10000,
            "size": 1,
            "mode": "dry_run",
        },
    )

    assert response.status_code == 200
    assert response.json()["data"]["status"] == "dry_run"

    intents = store.read(DatasetName.EXECUTION_ORDER_INTENT)
    events = store.read(DatasetName.EXECUTION_ORDER_EVENT, read_mode="all_runs")
    assert intents.height == 1
    assert events.height == 1
    assert intents.item(0, "account") == "main"
    assert events.item(0, "event_type") == "submit"


def test_execution_order_rejects_unknown_account(tmp_path, monkeypatch) -> None:
    monkeypatch.setenv("UFORIA_EXECUTION_ENABLED", "true")
    monkeypatch.setenv(
        "UFORIA_EXECUTION_ACCOUNTS_PATH",
        str(tmp_path / "config" / "missing.json"),
    )
    monkeypatch.setenv("UFORIA_EXECUTION_KILL_SWITCH", "false")

    store = DatasetStore(tmp_path)
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.post(
        "/api/v1/execution/order",
        json={
            "venue": "hyperliquid",
            "account": "unknown",
            "instrument_type": "perp",
            "asset": "BTC",
            "symbol": "BTC-PERP",
            "side": "buy",
            "order_type": "limit",
            "time_in_force": "gtc",
            "price": 100000,
            "size": 1,
            "mode": "dry_run",
        },
    )

    assert response.status_code == 200
    assert response.json()["data"]["status"] == "rejected"
    assert response.json()["data"]["failure_code"] == "auth_failure"


class _FakeLiveClient:
    def market_snapshots(self, asset=None):
        return [
            {
                "venue": "hyperliquid",
                "asset": asset or "BTC",
                "symbol": f"{asset or 'BTC'}-PERP",
                "instrument_type": "perp",
                "status": "online",
                "tick_size": 0.1,
                "step_size": 0.001,
                "min_size": 0.001,
                "min_notional": 10.0,
                "best_bid": 100000.0,
                "best_ask": 100010.0,
                "mark_price": 100005.0,
                "index_price": 100002.0,
                "quality_flag": "ok",
            }
        ]

    def account_snapshot(self, asset=None):
        ts = datetime(2026, 3, 12, 12, tzinfo=timezone.utc)
        return type(
            "Snapshot",
            (),
            {
                "balances": [
                    {
                        "ts": ts,
                        "venue": "hyperliquid",
                        "account": "main",
                        "asset": "USDC",
                        "balance_type": "total",
                        "total": 1000.0,
                        "available": 900.0,
                        "locked": 100.0,
                        "quality_flag": "ok",
                    }
                ],
                "positions": [],
                "orders": [],
                "fills": [],
                "health": None,
            },
        )()

    def request_quote(self, request):
        return type(
            "Quote",
            (),
            {
                "quote_id": "quote-1",
                "message": "live quote ok",
                "payload": {"quote": True},
                "price": 100005.0,
                "expires_at": datetime(2026, 3, 12, 12, 5, tzinfo=timezone.utc),
                "route": "router",
            },
        )()

    def place_order(self, intent):
        return type(
            "Order",
            (),
            {
                "venue_order_id": "oid-1",
                "message": "live order ok",
                "payload": {"live": True},
                "status": "accepted",
                "filled_size": 0.25,
                "remaining_size": 0.75,
                "avg_fill_price": 100001.0,
                "fill_events": [
                    {
                        "ts": datetime(2026, 3, 12, 12, tzinfo=timezone.utc),
                        "fill_id": "fill-1",
                        "request_id": intent.request_id,
                        "venue_order_id": "oid-1",
                        "venue": "hyperliquid",
                        "account": "main",
                        "asset": "BTC",
                        "symbol": "BTC-PERP",
                        "side": "buy",
                        "price": 100001.0,
                        "size": 0.25,
                        "fee": 1.0,
                        "fee_asset": "USDC",
                        "liquidity": "taker",
                        "payload": {"fill": True},
                        "quality_flag": "ok",
                    }
                ],
            },
        )()

    def cancel(self, request):
        return type(
            "Cancel",
            (),
            {
                "venue_order_id": request.venue_order_id or "oid-1",
                "message": "live cancel ok",
                "payload": {"cancel": True},
                "status": "canceled",
            },
        )()

    def cancel_all(self, request):
        return type(
            "CancelAll",
            (),
            {
                "venue_order_id": "cancel-all-1",
                "message": "live cancel-all ok",
                "payload": {"cancel_all": True},
                "status": "canceled",
            },
        )()


def test_execution_order_live_uses_live_client(tmp_path, monkeypatch) -> None:
    _write_account_config(tmp_path)
    monkeypatch.setenv("UFORIA_EXECUTION_ENABLED", "true")
    monkeypatch.setenv(
        "UFORIA_EXECUTION_ACCOUNTS_PATH",
        str(tmp_path / "config" / "execution_accounts.json"),
    )
    monkeypatch.setenv("UFORIA_EXECUTION_KILL_SWITCH", "false")
    monkeypatch.setenv("UFORIA_EXECUTION_MAX_ORDER_NOTIONAL_USD", "200000")
    monkeypatch.setattr(
        "uforia.execution.service.create_live_client",
        lambda **_: _FakeLiveClient(),
    )

    store = DatasetStore(tmp_path)
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.post(
        "/api/v1/execution/order",
        json={
            "venue": "hyperliquid",
            "account": "main",
            "instrument_type": "perp",
            "asset": "BTC",
            "symbol": "BTC-PERP",
            "side": "buy",
            "order_type": "limit",
            "time_in_force": "gtc",
            "price": 100000,
            "size": 1,
            "mode": "live",
        },
    )

    assert response.status_code == 200
    assert response.json()["data"]["status"] == "accepted"
    events = store.read(DatasetName.EXECUTION_ORDER_EVENT, read_mode="all_runs")
    fills = store.read(DatasetName.EXECUTION_FILL_EVENT, read_mode="all_runs")
    balances = store.read(DatasetName.EXECUTION_BALANCE_SNAPSHOT)
    assert events.height >= 1
    assert fills.height == 1
    assert balances.height == 1


def test_execution_quote_live_uses_live_client(tmp_path, monkeypatch) -> None:
    _write_account_config(tmp_path)
    monkeypatch.setenv("UFORIA_EXECUTION_ENABLED", "true")
    monkeypatch.setenv(
        "UFORIA_EXECUTION_ACCOUNTS_PATH",
        str(tmp_path / "config" / "execution_accounts.json"),
    )
    monkeypatch.setenv("UFORIA_EXECUTION_KILL_SWITCH", "false")
    monkeypatch.setenv("UFORIA_EXECUTION_MAX_ORDER_NOTIONAL_USD", "200000")
    monkeypatch.setattr(
        "uforia.execution.service.create_live_client",
        lambda **_: _FakeLiveClient(),
    )

    store = DatasetStore(tmp_path)
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.post(
        "/api/v1/execution/quote",
        json={
            "venue": "hyperliquid",
            "account": "main",
            "asset": "BTC",
            "symbol": "BTC-PERP",
            "side": "buy",
            "size": 1,
            "mode": "live",
        },
    )

    assert response.status_code == 200
    assert response.json()["data"]["status"] == "accepted"
    quotes = store.read(DatasetName.EXECUTION_QUOTE_SNAPSHOT, read_mode="all_runs")
    assert quotes.height == 1
    assert quotes.item(0, "quote_id") == "quote-1"


def test_execution_sync_live_state_writes_market_and_balance_rows(tmp_path, monkeypatch) -> None:
    _write_account_config(tmp_path)
    monkeypatch.setenv("UFORIA_EXECUTION_ENABLED", "true")
    monkeypatch.setenv(
        "UFORIA_EXECUTION_ACCOUNTS_PATH",
        str(tmp_path / "config" / "execution_accounts.json"),
    )
    monkeypatch.setenv("UFORIA_EXECUTION_KILL_SWITCH", "false")
    monkeypatch.setenv("UFORIA_EXECUTION_MAX_ORDER_NOTIONAL_USD", "200000")
    monkeypatch.setattr(
        "uforia.execution.service.create_live_client",
        lambda **_: _FakeLiveClient(),
    )

    store = DatasetStore(tmp_path)
    service = ExecutionService(store, get_config())

    written = service.sync_live_state(venue="hyperliquid", account="main", asset="BTC")

    markets = store.read(DatasetName.EXECUTION_MARKET_SNAPSHOT)
    balances = store.read(DatasetName.EXECUTION_BALANCE_SNAPSHOT)

    assert written["markets"] >= 1
    assert written["balances"] >= 1
    assert markets.height == 1
    assert balances.height == 1
