from __future__ import annotations

from datetime import datetime, timedelta, timezone

import polars as pl

from uforia.analytics import (
    build_perps_composite_series,
    build_perps_signal_series,
    build_perps_transition_series,
)
from uforia.storage import DatasetName, DatasetStore


def test_perps_family_builders_produce_rows(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    _seed_perps_context(store)

    start = datetime(2026, 2, 28, 0, tzinfo=timezone.utc)
    end = start + timedelta(hours=8)

    signals = build_perps_signal_series(
        store,
        start=start,
        end=end,
        asset="BTC",
    )
    assert signals.height > 0
    assert {
        "carry_annualized_pct",
        "cascade_risk_score",
        "vpin_50",
        "oi_price_divergence",
        "lead_lag_score",
        "depth_recovery_speed",
        "vol_signature_ratio",
        "cum_delta_acceleration",
    } <= set(signals.columns)
    assert signals["quality_flag"].null_count() == 0
    assert signals["basis_bps"].null_count() < signals.height

    store.write(DatasetName.PERPS_SIGNAL_SERIES, signals)
    composites = build_perps_composite_series(
        store,
        start=start,
        end=end,
        asset="BTC",
    )
    transitions = build_perps_transition_series(
        store,
        start=start,
        end=end,
        asset="BTC",
    )

    assert composites.height > 0
    assert transitions.height > 0
    assert {
        "carry_score",
        "cascade_risk_score",
        "toxicity_score",
        "positioning_score",
        "fragility_score",
    } <= set(composites.columns)
    assert "next_state_distribution" in transitions.columns


def _seed_perps_context(store: DatasetStore) -> None:
    base = datetime(2026, 2, 28, 0, tzinfo=timezone.utc)
    bars_1m: list[dict[str, object]] = []
    bars_5s: list[dict[str, object]] = []
    raw_rows: list[dict[str, object]] = []
    book_rows: list[dict[str, object]] = []
    funding_rows: list[dict[str, object]] = []
    venues = (
        ("binance_spot", "spot", False, "spot", 1.0),
        ("binance_futures", "futures", True, "linear_future", 1.003),
        ("bybit_spot", "spot", False, "spot", 1.0005),
        ("bybit_perps", "perp", True, "linear_perp", 1.0025),
    )

    for minute in range(420):
        ts = base + timedelta(minutes=minute)
        spot = 60_000.0 + minute * 2.5
        for venue, market_type, is_derivatives, contract_type, mult in venues:
            close = spot * mult
            buy_volume = 100.0 + (minute % 7) * 4.0 + (6.0 if is_derivatives else 2.0)
            sell_volume = 95.0 + (minute % 5) * 3.0 + (4.0 if is_derivatives else 1.0)
            spread = 4.0 + (minute % 11) * 0.15 + (0.8 if is_derivatives else 0.2)
            depth_bid_5 = 800_000.0 + minute * 85.0 + (5_000.0 if is_derivatives else 0.0)
            depth_ask_5 = 780_000.0 + minute * 80.0 + (4_000.0 if is_derivatives else 0.0)
            venue_group = "binance" if venue.startswith("binance") else "bybit"
            bars_1m.append(
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": venue,
                    "exchange_symbol": venue,
                    "venue_group": venue_group,
                    "market_type": market_type,
                    "is_derivatives": is_derivatives,
                    "contract_type": contract_type,
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": close - 4.0,
                    "high": close + 8.0,
                    "low": close - 8.0,
                    "close": close,
                    "volume": buy_volume + sell_volume,
                    "buy_volume": buy_volume,
                    "sell_volume": sell_volume,
                    "trade_count": 50 + (minute % 9),
                    "vwap": close - 1.0,
                    "microprice": close + 0.5,
                    "spread": spread,
                    "depth_bid_5": depth_bid_5,
                    "depth_ask_5": depth_ask_5,
                    "depth_notional_bid_5": depth_bid_5 * close,
                    "depth_notional_ask_5": depth_ask_5 * close,
                    "is_gap": False,
                    "quality_flag": "ok",
                }
            )
            book_rows.append(
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": venue,
                    "exchange_symbol": venue,
                    "venue_group": venue_group,
                    "market_type": market_type,
                    "is_derivatives": is_derivatives,
                    "contract_type": contract_type,
                    "best_bid": close - 2.0,
                    "best_bid_size": 25.0,
                    "best_ask": close + 2.0,
                    "best_ask_size": 24.0,
                    "mid_price": close,
                    "microprice": close + 0.5,
                    "spread": spread,
                    "spread_rel": spread / close,
                    "depth_bid_5": depth_bid_5,
                    "depth_ask_5": depth_ask_5,
                    "depth_notional_bid_5": depth_bid_5 * close,
                    "depth_notional_ask_5": depth_ask_5 * close,
                    "imbalance_5": 0.1 if is_derivatives else 0.02,
                    "is_seeded": True,
                    "is_sequence_valid": True,
                    "is_gap_detected": False,
                    "reseed_count": 0,
                    "book_quality": "complete",
                    "quality_flag": "ok",
                }
            )
            raw_rows.append(
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": venue,
                    "event_type": "trade",
                    "event_id": f"{venue}-trade-{minute}",
                    "exchange_symbol": venue,
                    "venue_group": venue_group,
                    "market_type": market_type,
                    "is_derivatives": is_derivatives,
                    "contract_type": contract_type,
                    "exchange_ts": ts,
                    "received_ts": ts,
                    "local_ts": ts,
                    "transaction_ts": ts,
                    "side": "buy" if minute % 2 == 0 else "sell",
                    "liquidation_side": None,
                    "price": close,
                    "size": 5.0 + (minute % 4),
                    "trade_notional": close * (5.0 + (minute % 4)),
                    "bid": close - 2.0,
                    "ask": close + 2.0,
                    "mark_price": close,
                    "open_interest": 1_000_000.0 + minute * 150.0 if is_derivatives else None,
                    "depth_notional_bid_5": depth_bid_5 * close,
                    "depth_notional_ask_5": depth_ask_5 * close,
                    "first_update_id": minute,
                    "last_update_id": minute + 1,
                    "payload": "{}",
                    "quality_flag": "ok",
                }
            )
            if is_derivatives:
                raw_rows.append(
                    {
                        "ts": ts,
                        "date": ts.date(),
                        "asset": "BTC",
                        "venue": venue,
                        "event_type": "liquidation",
                        "event_id": f"{venue}-liq-{minute}",
                        "exchange_symbol": venue,
                        "venue_group": venue_group,
                        "market_type": market_type,
                        "is_derivatives": True,
                        "contract_type": contract_type,
                        "exchange_ts": ts,
                        "received_ts": ts,
                        "local_ts": ts,
                        "transaction_ts": ts,
                        "side": "sell",
                        "liquidation_side": "long" if minute % 3 == 0 else "short",
                        "price": close,
                        "size": 1.5 + (minute % 3),
                        "trade_notional": close * (1.5 + (minute % 3)),
                        "bid": close - 2.0,
                        "ask": close + 2.0,
                        "mark_price": close,
                        "open_interest": 1_000_000.0 + minute * 150.0,
                        "depth_notional_bid_5": depth_bid_5 * close,
                        "depth_notional_ask_5": depth_ask_5 * close,
                        "first_update_id": minute,
                        "last_update_id": minute + 1,
                        "payload": "{}",
                        "quality_flag": "ok",
                    }
                )
        for tick in range(12):
            ts_5s = ts + timedelta(seconds=tick * 5)
            for venue, market_type, is_derivatives, contract_type, mult in venues:
                close = (spot + tick * 0.25) * mult
                depth_bid_5 = 805_000.0 + minute * 85.0
                depth_ask_5 = 785_000.0 + minute * 80.0
                venue_group = "binance" if venue.startswith("binance") else "bybit"
                bars_5s.append(
                    {
                        "ts": ts_5s,
                        "date": ts_5s.date(),
                        "asset": "BTC",
                        "venue": venue,
                        "exchange_symbol": venue,
                        "venue_group": venue_group,
                        "market_type": market_type,
                        "is_derivatives": is_derivatives,
                        "contract_type": contract_type,
                        "clock": "event_time",
                        "bar_size_secs": 5,
                        "open": close - 1.0,
                        "high": close + 1.5,
                        "low": close - 1.5,
                        "close": close,
                        "volume": 30.0,
                        "buy_volume": 16.0,
                        "sell_volume": 14.0,
                        "trade_count": 6,
                        "vwap": close,
                        "microprice": close + 0.1,
                        "spread": 2.0,
                        "depth_bid_5": depth_bid_5,
                        "depth_ask_5": depth_ask_5,
                        "depth_notional_bid_5": depth_bid_5 * close,
                        "depth_notional_ask_5": depth_ask_5 * close,
                        "is_gap": False,
                        "quality_flag": "ok",
                    }
                )
        funding_rows.extend(
            [
                {
                    "ts": ts,
                    "date": ts.date(),
                    "venue": "binance_futures",
                    "underlying": "BTC",
                    "source": "microstructure_live",
                    "interest_1h": 0.000001 + minute * 1e-9,
                    "interest_8h": None,
                    "quality_flag": "ok",
                },
                {
                    "ts": ts,
                    "date": ts.date(),
                    "venue": "bybit_perps",
                    "underlying": "BTC",
                    "source": "microstructure_live",
                    "interest_1h": 0.0000012 + minute * 1e-9,
                    "interest_8h": None,
                    "quality_flag": "ok",
                },
            ]
        )

    store.write(DatasetName.MICROSTRUCTURE_BAR, pl.DataFrame(bars_1m))
    store.write(DatasetName.MICROSTRUCTURE_BAR, pl.DataFrame(bars_5s))
    store.write(DatasetName.MICROSTRUCTURE_RAW_EVENT, pl.DataFrame(raw_rows))
    store.write(DatasetName.MICROSTRUCTURE_BOOK_STATE, pl.DataFrame(book_rows))
    store.write(DatasetName.FUNDING_RATE_SNAPSHOT, pl.DataFrame(funding_rows))
