#!/usr/bin/env python3
"""Decisive A/B: LGBM top-N vs v13e top-N realized fwd_return_10d on v13e-eligible subset.

If LGBM top-N realized >= v13e top-N realized on test, integration is worth building.
Else Option A is dead.
"""
from __future__ import annotations

import sys
from pathlib import Path

import numpy as np
import pandas as pd
import pyarrow.parquet as pq
from lightgbm import LGBMClassifier

sys.path.insert(0, ".")
from libs.backtest.scoring import compute_return_max_long_score_v13e

NUMERIC = [
    "reaction_day_return", "gap_size", "close_location", "volume_ratio_20d",
    "document_quality_score", "parse_confidence_overall", "oneoff_penalty",
    "market_cap_proxy", "avg_dollar_volume_20d",
    "pre_event_volatility_20d", "pre_event_rsi_14", "pre_event_bb_position",
    "pre_event_obv_slope_20d",
    "macro_vix", "macro_hy_spread", "macro_t10y2y", "prior_event_fwd5d",
    "lm_positive_pct", "lm_negative_pct", "lm_net_sentiment",
    "earnings_surprise_pct",
    "sue_lag_1_pct", "sue_lag_2_pct", "sue_lag_3_pct",
    "sue_hist_mean_4q", "sue_hist_mean_8q", "sue_hist_mean_12q",
    "sue_hist_pos_rate_4q", "sue_hist_pos_rate_12q",
    "sue_hist_latest_pct", "sue_hist_streak_pos",
    "peer_sector_event_count_365d", "peer_sector_surprise_median_365d",
    "peer_sector_surprise_mean_365d", "peer_sector_surprise_pos_rate_365d",
    "peer_relative_surprise_pct_365d",
    "peer_sector_sue_hist_mean_4q_median_365d",
    "peer_sector_sue_hist_mean_4q_mean_365d",
    "peer_relative_sue_hist_mean_4q_365d",
    "peer_sector_sue_hist_pos_rate_4q_mean_365d",
    "pre_event_short_ratio", "pre_event_sector_momentum_20d",
    "prior_catalyst_count_60d", "prior_catalyst_type_diversity_60d",
    "price_vs_sma20", "pre_event_momentum_20d",
]
CAT = ["event_type", "event_direction", "guidance_status", "sector"]


def load(root: Path, split: str) -> pd.DataFrame:
    schema = pq.read_schema(root / f"{split}.parquet")
    cols = [c for c in NUMERIC + CAT + ["mfe_10d", "mae_10d", "fwd_return_10d"] if c in schema.names]
    df = pq.read_table(root / f"{split}.parquet", columns=cols).to_pandas()
    for c in NUMERIC:
        if c not in df.columns:
            df[c] = np.nan
        df[c] = pd.to_numeric(df[c], errors="coerce")
    for c in CAT:
        if c not in df.columns:
            df[c] = "missing"
        df[c] = df[c].fillna("missing").astype("category")
    df["target"] = ((df["mfe_10d"] - df["mae_10d"]) > 0.08).astype(int)
    return df


def main():
    root = Path("data/parquet/midlarge-liquid-long-v1_bucketfix_full_audit_canonical_ftb_fix_v2")
    train = load(root, "train")
    valid = load(root, "valid")
    test = load(root, "test")

    feats = NUMERIC + CAT
    model = LGBMClassifier(
        n_estimators=400, learning_rate=0.03, num_leaves=31,
        min_child_samples=50, subsample=0.8, colsample_bytree=0.8,
        objective="binary", random_state=42, verbosity=-1,
    )
    model.fit(train[feats], train["target"], categorical_feature=CAT)

    for split_name, df in [("valid", valid), ("test", test)]:
        df = df.copy()
        df["lgbm"] = model.predict_proba(df[feats])[:, 1]
        df["v13e"] = [compute_return_max_long_score_v13e(r) for r in df.to_dict("records")]
        elig = df[df["v13e"] >= 0.42].copy()
        n_trades = {"valid": 65, "test": 37}.get(split_name, 30)
        N = min(n_trades, len(elig))
        v13e_top = elig.nlargest(N, "v13e")
        lgbm_top = elig.nlargest(N, "lgbm")
        elig_mean = elig["fwd_return_10d"].mean()
        v13e_mean = v13e_top["fwd_return_10d"].mean()
        lgbm_mean = lgbm_top["fwd_return_10d"].mean()
        v13e_hit = v13e_top["target"].mean()
        lgbm_hit = lgbm_top["target"].mean()
        print(f"== {split_name} (eligible n={len(elig)}, top-N={N}) ==")
        print(f"  eligible mean fwd10d:        {elig_mean:+.4f}")
        print(f"  v13e top-N mean fwd10d:      {v13e_mean:+.4f}  hit={v13e_hit:.3f}")
        print(f"  lgbm top-N mean fwd10d:      {lgbm_mean:+.4f}  hit={lgbm_hit:.3f}")
        delta = lgbm_mean - v13e_mean
        verdict = "LGBM WINS" if delta > 0.005 else ("LGBM TIES" if abs(delta) < 0.005 else "LGBM LOSES")
        print(f"  delta (lgbm - v13e):         {delta:+.4f}  -> {verdict}")
        # also overlap
        v13e_set = set(v13e_top.index)
        lgbm_set = set(lgbm_top.index)
        overlap = len(v13e_set & lgbm_set)
        print(f"  index overlap: {overlap}/{N} ({100*overlap/N:.1f}%)")
        print()


if __name__ == "__main__":
    main()
