from __future__ import annotations

from datetime import datetime, timedelta, timezone

import polars as pl

from uforia.config import AppConfig, get_config
from uforia.ingest.deribit.client import DeribitPublicClient


def fetch_dvol(
    start: datetime,
    end: datetime,
    underlying: str = "BTC",
    client: DeribitPublicClient | None = None,
    config: AppConfig | None = None,
) -> pl.DataFrame:
    cfg = config or get_config()
    deribit = client or DeribitPublicClient(cfg)
    normalized_start = start.astimezone(timezone.utc).replace(minute=0, second=0, microsecond=0)
    normalized_end = end.astimezone(timezone.utc).replace(minute=0, second=0, microsecond=0)
    if normalized_end < normalized_start:
        raise ValueError("end must be greater than or equal to start")

    chunks: list[pl.DataFrame] = []
    cursor = normalized_end
    while cursor >= normalized_start:
        window_start = max(normalized_start, cursor - timedelta(days=30))
        result = deribit.get_volatility_index_data(
            underlying, window_start, cursor, resolution="3600"
        )
        rows = result.get("data", [])
        if not rows:
            break
        chunk = pl.DataFrame(
            {
                "ts": [datetime.fromtimestamp(item[0] / 1000, tz=timezone.utc) for item in rows],
                "date": [
                    datetime.fromtimestamp(item[0] / 1000, tz=timezone.utc).date() for item in rows
                ],
                "venue": ["deribit"] * len(rows),
                "underlying": [underlying] * len(rows),
                "source": ["dvol"] * len(rows),
                # Deribit returns DVOL in index points already, e.g. 60.89 rather than 0.6089.
                "dvol_open": [float(item[1]) for item in rows],
                "dvol_high": [float(item[2]) for item in rows],
                "dvol_low": [float(item[3]) for item in rows],
                "dvol_close": [float(item[4]) for item in rows],
                "quality_flag": ["ok"] * len(rows),
            }
        )
        chunks.append(chunk)
        continuation = result.get("continuation")
        if continuation is None:
            break
        cursor = datetime.fromtimestamp(int(continuation) / 1000, tz=timezone.utc) - timedelta(
            hours=1
        )

    if not chunks:
        return pl.DataFrame()
    return pl.concat(chunks, how="vertical_relaxed").unique(subset=["ts"]).sort("ts")
