from __future__ import annotations
import polars as pl
import numpy as np
from typing import Dict


# Underlying-level quality checks
def underlying_quality_report(df: pl.DataFrame) -> Dict[str, float]:
    """
    Generate quality metrics for underlying daily data.
    """

    report = {}

    # Missingness
    for col in df.columns:
        if col in ("symbol", "date"):
            continue

        miss = df.select(pl.col(col).null_count()).item()
        report[f"missing_frac__{col}"] = miss / len(df)

    # Sanity checks
    if "px_close" in df.columns:
        report["neg_price_frac"] = (
            df.filter(pl.col("px_close") <= 0).height / len(df)
        )

    if "volume" in df.columns:
        report["neg_volume_frac"] = (
            df.filter(pl.col("volume") < 0).height / len(df)
        )

    if "vix" in df.columns:
        report["neg_vix_frac"] = (
            df.filter(pl.col("vix") <= 0).height / len(df)
        )

    # IV sanity
    iv_cols = [c for c in df.columns if c.startswith("iv_")]
    if iv_cols:
        report["neg_iv_frac"] = (
            df.filter(
                pl.any_horizontal([pl.col(c) <= 0 for c in iv_cols])
            ).height / len(df)
        )

    # Rate sanity
    rate_cols = [c for c in df.columns if c.startswith("rate_")]
    if rate_cols:
        report["absurd_rate_frac"] = (
            df.filter(
                pl.any_horizontal(
                    [(pl.col(c) < -0.05) | (pl.col(c) > 0.20) for c in rate_cols]
                )
            ).height / len(df)
        )

    return report


# Option-level quality checks
def option_quality_report(df: pl.DataFrame) -> Dict[str, float]:
    """
    Generate quality metrics for per-option Greeks.
    """

    report = {}

    n = len(df)
    if n == 0:
        return {"empty": 1.0}

    # Basic sanity
    report["neg_sigma_frac"] = (
        df.filter(pl.col("sigma") <= 0).height / n
    )

    report["nan_sigma_frac"] = (
        df.filter(pl.col("sigma").is_null()).height / n
    )

    # Greek bounds
    report["absurd_delta_frac"] = (
        df.filter(pl.col("delta").abs() > 1.01).height / n
    )

    report["neg_gamma_frac"] = (
        df.filter(pl.col("gamma") < 0).height / n
    )

    report["absurd_vega_frac"] = (
        df.filter(pl.col("vega").abs() > 10).height / n
    )

    # Expiry sanity
    if "days_to_expiry" in df.columns:
        report["expired_frac"] = (
            df.filter(pl.col("days_to_expiry") <= 0).height / n
        )

    return report



# Row-level flags (optional, non-destructive)
def flag_option_rows(df: pl.DataFrame) -> pl.DataFrame:
    """
    Attach boolean quality flags to per-option rows.
    """

    return df.with_columns([
        (pl.col("sigma") <= 0).alias("flag_bad_sigma"),
        (pl.col("delta").abs() > 1.01).alias("flag_bad_delta"),
        (pl.col("gamma") < 0).alias("flag_bad_gamma"),
        (pl.col("days_to_expiry") <= 0).alias("flag_expired"),
    ])
