from __future__ import annotations

from datetime import datetime, timedelta, 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.storage import DatasetName, DatasetStore


def test_api_endpoints_return_signal_and_run_data(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    base = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.DVOL_SNAPSHOT,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "venue": "deribit",
                    "underlying": "BTC",
                    "source": "dvol",
                    "dvol_open": 50.0,
                    "dvol_high": 55.0,
                    "dvol_low": 49.0,
                    "dvol_close": 54.0,
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    store.write(
        DatasetName.VVIX_SERIES,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "underlying": "BTC",
                    "source": "dvol",
                    "vol_index_value": 54.0,
                    "rvvix_7d": 90.0,
                    "rvvix_14d": 80.0,
                    "rvvix_30d": 70.0,
                    "qvvix_30d": 65.0,
                    "model_version": "qvvix-var-v1",
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    store.write(
        DatasetName.SURFACE_FEATURES,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "underlying": "BTC",
                    "source": "ssvi",
                    "atm_iv_7d": 0.5,
                    "atm_iv_14d": 0.52,
                    "atm_iv_30d": 0.55,
                    "atm_iv_60d": 0.58,
                    "rr_25_7d": -0.05,
                    "rr_25_30d": -0.03,
                    "bf_25_7d": 0.02,
                    "bf_25_30d": 0.03,
                    "term_slope_7_30": -0.05,
                    "term_slope_30_60": -0.03,
                    "fit_quality": 0.01,
                    "coverage_ratio": 0.8,
                    "maturity_violations": 0,
                    "fit_validity_flag": "ok",
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    store.write(
        DatasetName.INGESTION_RUNS,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "dataset": DatasetName.OPTION_CHAIN_SNAPSHOT.value,
                    "venue": "deribit",
                    "underlying": "BTC",
                    "status": "success",
                    "rows_written_raw": 1,
                    "rows_written_normalized": 2,
                    "error_message": None,
                    "duration_ms": 100,
                }
            ]
        ),
        logical_ts=base,
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    latest = client.get("/api/v1/signals/btc_vvix/latest", params={"source": "dvol"})
    series = client.get(
        "/api/v1/signals/btc_vvix/series",
        params={
            "source": "dvol",
            "start": (base - timedelta(days=1)).isoformat(),
            "end": base.isoformat(),
        },
    )
    runs = client.get("/api/v1/runs")
    health = client.get("/api/v1/system/health")

    assert latest.status_code == 200
    assert latest.json()["data"]["latest_values"]["dvol"] == 54.0
    assert series.status_code == 200
    assert len(series.json()["data"]["points"]) == 1
    assert client.get("/api/v1/signals").status_code == 200
    assert runs.status_code == 200
    assert runs.json()["data"][0]["status"] == "success"
    assert health.status_code == 200
    assert health.json()["data"]["status"] == "ok"


def test_api_websocket_stream_emits_updates(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    base = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.VVIX_SERIES,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "underlying": "BTC",
                    "source": "dvol",
                    "vol_index_value": 54.0,
                    "rvvix_7d": 90.0,
                    "rvvix_14d": 80.0,
                    "rvvix_30d": 70.0,
                    "qvvix_30d": 65.0,
                    "model_version": "qvvix-var-v1",
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    store.write(
        DatasetName.DVOL_SNAPSHOT,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "venue": "deribit",
                    "underlying": "BTC",
                    "source": "dvol",
                    "dvol_open": 50.0,
                    "dvol_high": 55.0,
                    "dvol_low": 49.0,
                    "dvol_close": 54.0,
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    app = create_app(
        settings=ApiSettings(data_root=tmp_path, stream_interval_seconds=0.01),
        store=store,
    )
    client = TestClient(app)

    with client.websocket_connect("/api/v1/stream") as websocket:
        message = websocket.receive_json()

    assert message["event"] in {
        "signal.updated",
        "dataset.updated",
        "engine.updated",
        "run.updated",
    }


def test_price_series_falls_back_to_latest_available_bars(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    latest = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    earlier = latest - timedelta(minutes=15)
    store.write(
        DatasetName.MICROSTRUCTURE_BAR,
        pl.DataFrame(
            [
                {
                    "ts": earlier,
                    "date": earlier.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 100.0,
                    "high": 101.0,
                    "low": 99.0,
                    "close": 100.5,
                    "volume": 10.0,
                    "buy_volume": 6.0,
                    "sell_volume": 4.0,
                    "trade_count": 5,
                    "vwap": 100.4,
                    "microprice": 100.45,
                    "spread": 0.5,
                    "depth_bid_5": 1.0,
                    "depth_ask_5": 1.1,
                    "depth_notional_bid_5": 100.5,
                    "depth_notional_ask_5": 110.55,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
                {
                    "ts": latest,
                    "date": latest.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 100.5,
                    "high": 102.0,
                    "low": 100.0,
                    "close": 101.5,
                    "volume": 12.0,
                    "buy_volume": 7.0,
                    "sell_volume": 5.0,
                    "trade_count": 6,
                    "vwap": 101.2,
                    "microprice": 101.3,
                    "spread": 0.45,
                    "depth_bid_5": 1.2,
                    "depth_ask_5": 1.3,
                    "depth_notional_bid_5": 121.8,
                    "depth_notional_ask_5": 131.95,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.get(
        "/api/v1/prices/series",
        params={
            "asset": "BTC",
            "timeframe": "15m",
            "start": "2026-03-05T00:00:00Z",
            "end": "2026-03-05T02:00:00Z",
            "max_points": 10,
        },
    )

    assert response.status_code == 200
    payload = response.json()["data"]
    assert len(payload["points"]) == 2
    assert payload["latest_values"]["close"] == 101.5
    assert payload["context"]["stale"] is True
    assert payload["context"]["latest_available_at"].startswith("2026-03-01T12:00:00")


def test_latest_prices_fall_back_to_microstructure_bars(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    ts = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.MICROSTRUCTURE_BAR,
        pl.DataFrame(
            [
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "ETH",
                    "venue": "bybit_spot",
                    "exchange_symbol": "ETHUSDT",
                    "venue_group": "bybit",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 2000.0,
                    "high": 2010.0,
                    "low": 1995.0,
                    "close": 2005.0,
                    "volume": 3.0,
                    "buy_volume": 2.0,
                    "sell_volume": 1.0,
                    "trade_count": 2,
                    "vwap": 2004.0,
                    "microprice": 2005.1,
                    "spread": 0.3,
                    "depth_bid_5": 2.0,
                    "depth_ask_5": 2.1,
                    "depth_notional_bid_5": 4010.0,
                    "depth_notional_ask_5": 4210.5,
                    "is_gap": False,
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.get("/api/v1/prices")

    assert response.status_code == 200
    payload = response.json()["data"]
    assert len(payload) == 1
    assert payload[0]["asset"] == "ETH"
    assert payload[0]["mid_price"] == 2005.0


def test_price_series_uses_robust_cross_venue_aggregation(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    ts = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.MICROSTRUCTURE_BAR,
        pl.DataFrame(
            [
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": "binance_futures",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "perps",
                    "is_derivatives": True,
                    "contract_type": "linear_perpetual",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 100.0,
                    "high": 100.0,
                    "low": 100.0,
                    "close": 100.0,
                    "volume": 20.0,
                    "buy_volume": 12.0,
                    "sell_volume": 8.0,
                    "trade_count": 10,
                    "vwap": 100.0,
                    "microprice": 100.0,
                    "spread": 0.2,
                    "depth_bid_5": 1.0,
                    "depth_ask_5": 1.0,
                    "depth_notional_bid_5": 100.0,
                    "depth_notional_ask_5": 100.0,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 110.0,
                    "high": 112.0,
                    "low": 109.0,
                    "close": 111.0,
                    "volume": 30.0,
                    "buy_volume": 17.0,
                    "sell_volume": 13.0,
                    "trade_count": 12,
                    "vwap": 110.5,
                    "microprice": 110.6,
                    "spread": 0.3,
                    "depth_bid_5": 1.4,
                    "depth_ask_5": 1.5,
                    "depth_notional_bid_5": 154.0,
                    "depth_notional_ask_5": 166.5,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": "bybit_perps",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "bybit",
                    "market_type": "perps",
                    "is_derivatives": True,
                    "contract_type": "linear_perpetual",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 110.5,
                    "high": 113.0,
                    "low": 108.5,
                    "close": 111.5,
                    "volume": 28.0,
                    "buy_volume": 16.0,
                    "sell_volume": 12.0,
                    "trade_count": 11,
                    "vwap": 111.1,
                    "microprice": 111.0,
                    "spread": 0.25,
                    "depth_bid_5": 1.3,
                    "depth_ask_5": 1.4,
                    "depth_notional_bid_5": 143.65,
                    "depth_notional_ask_5": 155.4,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": "bybit_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "bybit",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 111.0,
                    "high": 114.0,
                    "low": 109.5,
                    "close": 112.0,
                    "volume": 32.0,
                    "buy_volume": 18.0,
                    "sell_volume": 14.0,
                    "trade_count": 13,
                    "vwap": 112.0,
                    "microprice": 112.1,
                    "spread": 0.35,
                    "depth_bid_5": 1.6,
                    "depth_ask_5": 1.7,
                    "depth_notional_bid_5": 177.6,
                    "depth_notional_ask_5": 190.4,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.get(
        "/api/v1/prices/series",
        params={
            "asset": "BTC",
            "timeframe": "1m",
            "start": "2026-03-01T12:00:00Z",
            "end": "2026-03-01T12:01:00Z",
            "max_points": 10,
        },
    )

    assert response.status_code == 200
    payload = response.json()["data"]
    assert len(payload["points"]) == 1
    assert payload["points"][0]["open"] == 110.25
    assert payload["points"][0]["high"] == 112.5
    assert payload["points"][0]["low"] == 108.75
    assert payload["points"][0]["close"] == 111.25
    assert payload["context"]["aggregation_mode"] == "median_across_venues"
    assert payload["context"]["source_venue"] is None


def test_price_series_uses_preferred_binance_venue(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    ts = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.MICROSTRUCTURE_BAR,
        pl.DataFrame(
            [
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": "binance_futures",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "perps",
                    "is_derivatives": True,
                    "contract_type": "perpetual",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 100.0,
                    "high": 100.0,
                    "low": 100.0,
                    "close": 100.0,
                    "volume": 25.0,
                    "buy_volume": 13.0,
                    "sell_volume": 12.0,
                    "trade_count": 8,
                    "vwap": 100.0,
                    "microprice": 100.0,
                    "spread": 0.2,
                    "depth_bid_5": 1.0,
                    "depth_ask_5": 1.0,
                    "depth_notional_bid_5": 100.0,
                    "depth_notional_ask_5": 100.0,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
                {
                    "ts": ts,
                    "date": ts.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 110.0,
                    "high": 113.0,
                    "low": 109.0,
                    "close": 112.0,
                    "volume": 30.0,
                    "buy_volume": 16.0,
                    "sell_volume": 14.0,
                    "trade_count": 10,
                    "vwap": 111.2,
                    "microprice": 111.3,
                    "spread": 0.15,
                    "depth_bid_5": 1.1,
                    "depth_ask_5": 1.2,
                    "depth_notional_bid_5": 122.1,
                    "depth_notional_ask_5": 134.4,
                    "is_gap": False,
                    "quality_flag": "ok",
                },
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.get(
        "/api/v1/prices/series",
        params={
            "asset": "BTC",
            "venue_scope": "binance",
            "timeframe": "1m",
            "start": "2026-03-01T12:00:00Z",
            "end": "2026-03-01T12:01:00Z",
            "max_points": 10,
        },
    )

    assert response.status_code == 200
    payload = response.json()["data"]
    assert len(payload["points"]) == 1
    assert payload["points"][0]["open"] == 110.0
    assert payload["points"][0]["high"] == 113.0
    assert payload["points"][0]["low"] == 109.0
    assert payload["points"][0]["close"] == 112.0
    assert payload["context"]["aggregation_mode"] == "preferred_venue"
    assert payload["context"]["source_venue"] == "binance_spot"


def test_prices_uses_bounded_latest_book_snapshot(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    stale = datetime(2026, 3, 1, 11, 0, tzinfo=timezone.utc)
    latest = datetime(2026, 3, 1, 12, 0, tzinfo=timezone.utc)
    store.write(
        DatasetName.MICROSTRUCTURE_BOOK_STATE,
        pl.DataFrame(
            [
                {
                    "ts": stale,
                    "date": stale.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "best_bid": 70000.0,
                    "best_ask": 70001.0,
                    "best_bid_size": 1.0,
                    "best_ask_size": 1.2,
                    "mid_price": 70000.5,
                    "microprice": 70000.6,
                    "spread": 1.0,
                    "spread_rel": 1.0 / 70000.5,
                    "depth_bid_5": 2.0,
                    "depth_ask_5": 2.1,
                    "depth_notional_bid_5": 140001.0,
                    "depth_notional_ask_5": 147002.1,
                    "imbalance_5": 0.02,
                    "is_seeded": False,
                    "is_sequence_valid": True,
                    "is_gap_detected": False,
                    "reseed_count": 0,
                    "book_quality": "ok",
                    "quality_flag": "ok",
                },
                {
                    "ts": latest,
                    "date": latest.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "best_bid": 71000.0,
                    "best_ask": 71001.0,
                    "best_bid_size": 1.5,
                    "best_ask_size": 1.6,
                    "mid_price": 71000.5,
                    "microprice": 71000.55,
                    "spread": 1.0,
                    "spread_rel": 1.0 / 71000.5,
                    "depth_bid_5": 2.5,
                    "depth_ask_5": 2.6,
                    "depth_notional_bid_5": 177501.25,
                    "depth_notional_ask_5": 184602.6,
                    "imbalance_5": 0.03,
                    "is_seeded": False,
                    "is_sequence_valid": True,
                    "is_gap_detected": False,
                    "reseed_count": 0,
                    "book_quality": "ok",
                    "quality_flag": "ok",
                },
                {
                    "ts": latest,
                    "date": latest.date(),
                    "asset": "ETH",
                    "venue": "bybit_spot",
                    "exchange_symbol": "ETHUSDT",
                    "venue_group": "bybit",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "best_bid": 2000.0,
                    "best_ask": 2001.0,
                    "best_bid_size": 3.0,
                    "best_ask_size": 3.2,
                    "mid_price": 2000.5,
                    "microprice": 2000.55,
                    "spread": 1.0,
                    "spread_rel": 1.0 / 2000.5,
                    "depth_bid_5": 3.5,
                    "depth_ask_5": 3.6,
                    "depth_notional_bid_5": 7001.75,
                    "depth_notional_ask_5": 7201.8,
                    "imbalance_5": 0.04,
                    "is_seeded": False,
                    "is_sequence_valid": True,
                    "is_gap_detected": False,
                    "reseed_count": 0,
                    "book_quality": "ok",
                    "quality_flag": "ok",
                },
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.get("/api/v1/prices")

    assert response.status_code == 200
    payload = {row["asset"]: row for row in response.json()["data"]}
    assert payload["BTC"]["mid_price"] == 71000.5
    assert payload["BTC"]["venue"] == "binance_spot"
    assert payload["ETH"]["mid_price"] == 2000.5


def test_price_series_marks_in_range_data_as_stale(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    latest = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.MICROSTRUCTURE_BAR,
        pl.DataFrame(
            [
                {
                    "ts": latest,
                    "date": latest.date(),
                    "asset": "BTC",
                    "venue": "binance_spot",
                    "exchange_symbol": "BTCUSDT",
                    "venue_group": "binance",
                    "market_type": "spot",
                    "is_derivatives": False,
                    "contract_type": "spot",
                    "clock": "event_time",
                    "bar_size_secs": 60,
                    "open": 100.5,
                    "high": 102.0,
                    "low": 100.0,
                    "close": 101.5,
                    "volume": 12.0,
                    "buy_volume": 7.0,
                    "sell_volume": 5.0,
                    "trade_count": 6,
                    "vwap": 101.2,
                    "microprice": 101.3,
                    "spread": 0.45,
                    "depth_bid_5": 1.2,
                    "depth_ask_5": 1.3,
                    "depth_notional_bid_5": 121.8,
                    "depth_notional_ask_5": 131.95,
                    "is_gap": False,
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    app = create_app(settings=ApiSettings(data_root=tmp_path), store=store)
    client = TestClient(app)

    response = client.get(
        "/api/v1/prices/series",
        params={
            "asset": "BTC",
            "venue_scope": "binance",
            "timeframe": "15m",
            "start": "2026-03-01T11:00:00Z",
            "end": "2026-03-01T14:00:00Z",
            "max_points": 10,
        },
    )

    assert response.status_code == 200
    payload = response.json()["data"]
    assert payload["context"]["stale"] is True
    assert payload["context"]["latest_available_at"].startswith("2026-03-01T12:00:00")
    assert payload["context"]["source_venue"] == "binance_spot"


def test_api_exposes_eth_vvix_and_razor_signals(tmp_path) -> None:
    store = DatasetStore(tmp_path)
    base = datetime(2026, 3, 1, 12, tzinfo=timezone.utc)
    store.write(
        DatasetName.DVOL_SNAPSHOT,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "venue": "deribit",
                    "underlying": "ETH",
                    "source": "dvol",
                    "dvol_open": 60.0,
                    "dvol_high": 61.0,
                    "dvol_low": 59.0,
                    "dvol_close": 60.5,
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    store.write(
        DatasetName.VVIX_SERIES,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "underlying": "ETH",
                    "source": "dvol",
                    "vol_index_value": 60.5,
                    "rvvix_7d": 88.0,
                    "rvvix_14d": 77.0,
                    "rvvix_30d": 66.0,
                    "qvvix_30d": None,
                    "model_version": "rvvix-v1",
                    "quality_flag": "ok",
                }
            ]
        ),
    )
    store.write(
        DatasetName.RAZOR_SERIES,
        pl.DataFrame(
            [
                {
                    "ts": base,
                    "date": base.date(),
                    "underlying": "BTC",
                    "source": "synthetic_7d_atm",
                    "reference_tenor_days": 7,
                    "razor_pnl": 12.0,
                    "razor_vrp": 1.5,
                    "buyer_increment": 0.1,
                    "seller_increment": -0.1,
                    "option_reval_pnl": 0.05,
                    "hedge_pnl": 0.03,
                    "roll_pnl": 0.02,
                    "razor_pnl_zscore": 1.4,
                    "razor_vrp_zscore": 0.8,
                    "trade_band": "double_buy",
                    "long_vol_signal": True,
                    "short_vol_signal": False,
                    "quality_flag": "ok",
                }
            ]
        ),
    )

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

    signals = client.get("/api/v1/signals")
    eth_latest = client.get("/api/v1/signals/eth_vvix/latest", params={"source": "dvol"})
    razor_series = client.get(
        "/api/v1/signals/btc_razor/series",
        params={"source": "synthetic_7d_atm"},
    )

    assert signals.status_code == 200
    signal_ids = {item["id"] for item in signals.json()["data"]}
    assert {"btc_vvix", "eth_vvix", "btc_razor", "eth_razor"} <= signal_ids
    assert eth_latest.status_code == 200
    assert eth_latest.json()["data"]["symbol"] == "ETH"
    assert eth_latest.json()["data"]["latest_values"]["dvol"] == 60.5
    assert razor_series.status_code == 200
    assert razor_series.json()["data"]["points"][0]["trade_band"] == "double_buy"
