from __future__ import annotations

from datetime import datetime, timedelta

import polars as pl

from uforia.config import AppConfig, get_config
from uforia.storage import DatasetName, DatasetStore


def compute_rvvix(
    series: pl.DataFrame,
    windows: tuple[int, ...] = (7, 14, 30),
    cadence: str = "1h",
) -> pl.DataFrame:
    if cadence != "1h":
        raise ValueError("Only 1h cadence is supported in v1")
    if series.is_empty():
        return pl.DataFrame()
    frame = series.sort("ts").with_columns(
        pl.when(pl.col("value") > 0)
        .then(pl.col("value").log())
        .otherwise(None)
        .diff()
        .alias("log_return")
    )
    annualization = 24.0 * 365.0
    for window in windows:
        periods = window * 24
        frame = frame.with_columns(
            (
                pl.col("log_return").rolling_var(
                    window_size=periods, min_samples=max(2, periods // 2)
                )
                * annualization
            )
            .sqrt()
            .mul(100.0)
            .alias(f"rvvix_{window}d")
        )
    return frame


def compute_qvvix(
    store: DatasetStore,
    ts: datetime,
    source: str = "dvol",
    underlying: str = "BTC",
    horizon_days: int = 30,
    config: AppConfig | None = None,
) -> float:
    cfg = config or get_config()
    history_start = ts - timedelta(days=cfg.modeling.qvvix_history_days)
    vol_index = store.read(
        DatasetName.VOL_INDEX_SERIES,
        start=history_start,
        end=ts,
        filters={"underlying": underlying, "source": source},
    )
    cleaned = (
        vol_index.sort("ts")
        .filter(pl.col("value") > 0)
        .with_columns(pl.col("value").log().diff().alias("log_return"))
        .drop_nulls(["log_return"])
    )
    if cleaned.height < 24:
        raise ValueError("Not enough vol-index history for qVVIX")

    returns = cleaned["log_return"].to_list()
    ewma_lambda = cfg.modeling.qvvix_ewma_lambda
    variance = float(returns[0] ** 2)
    for ret in returns[1:]:
        variance = (ewma_lambda * variance) + ((1.0 - ewma_lambda) * float(ret**2))
    annualized = variance * 24.0 * 365.0
    return float((annualized**0.5) * 100.0)
