"""WFV-oriented research CLI for leader_intraday_momentum style strategies."""
from __future__ import annotations

import argparse
import asyncio
from datetime import datetime
from pathlib import Path
from typing import Any

from libs.common.config import get_settings

from apps.intraday_bt.momentum_research import (
    build_momentum_research_context,
    build_momentum_strategy,
    evaluate_momentum_wfv_candidate,
)
from apps.intraday_bt.oracle import make_intraday_oracle_client
from apps.intraday_bt.run import load_config


DEFAULT_CONFIG = "configs/intraday/strategies/leader_intraday_momentum.yaml"


def _leader_candidate_overrides() -> list[tuple[str, dict[str, Any]]]:
    """Curated nearby candidates around the current leader champion."""
    return [
        ("control", {}),
        ("top6", {"top_n": 6}),
        ("top6_trail_loose", {"top_n": 6, "trailing_stop_pct": -0.07}),
        (
            "top6_trail_loose_volume_ratio",
            {"top_n": 6, "trailing_stop_pct": -0.07, "min_volume_ratio_14d": 0.05},
        ),
        ("entropy_tight", {"max_entropy_20d": 0.88}),
        ("entropy_loose", {"max_entropy_20d": 0.92}),
        ("vix_tight", {"max_vix": 28.0}),
        ("vix_loose", {"max_vix": 32.0}),
        ("top4", {"top_n": 4}),
        ("trail_tight", {"trailing_stop_pct": -0.05}),
        ("trail_loose", {"trailing_stop_pct": -0.07}),
        (
            "volume_ratio_gate",
            {
                "min_volume_ratio_14d": 0.05,
            },
        ),
        (
            "spy_regime_guard",
            {
                "market_regime_spy_threshold": -0.005,
            },
        ),
        (
            "quality_defensive",
            {
                "top_n": 4,
                "max_entropy_20d": 0.88,
                "max_vix": 28.0,
                "trailing_stop_pct": -0.05,
            },
        ),
    ]


def _rank_results(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    rows.sort(
        key=lambda row: (
            row["score"]["selection_score"],
            row["score"]["holdout_return_pct"] or float("-inf"),
            row["score"]["mean_test_return_pct"] or float("-inf"),
        ),
        reverse=True,
    )
    for idx, row in enumerate(rows, start=1):
        row["rank"] = idx
    return rows


def _condensed_row(label: str, payload: dict[str, Any]) -> dict[str, Any]:
    score = payload["score"]
    summary = payload["walk_forward_summary"]
    holdout = payload["holdout_metrics"] or {}
    return {
        "label": label,
        "strategy": payload["strategy"],
        "score": score,
        "fold_count": summary["fold_count"],
        "holdout_trades": holdout.get("total_trades"),
        "holdout_max_dd_pct": (
            None
            if holdout.get("max_drawdown_pct") is None
            else round(abs(holdout["max_drawdown_pct"]) * 100.0, 2)
        ),
    }


async def main_async() -> None:
    parser = argparse.ArgumentParser(description="WFV research for leader intraday momentum")
    parser.add_argument("--config", default=DEFAULT_CONFIG)
    parser.add_argument("--wfv-start", default="2025-01-02")
    parser.add_argument("--wfv-end", default="2025-12-31")
    parser.add_argument("--holdout-start", default="2026-01-02")
    parser.add_argument("--holdout-end", default="2026-03-31")
    parser.add_argument("--train-days", type=int, default=84)
    parser.add_argument("--test-days", type=int, default=21)
    parser.add_argument("--step-days", type=int, default=21)
    parser.add_argument("--output-dir", default="runs/intraday/research")
    args = parser.parse_args()

    config = load_config(args.config)
    if config.strategy_mode != "momentum":
        raise ValueError("momentum_wfv only supports momentum configs")

    settings = get_settings()
    async with make_intraday_oracle_client(settings) as client:
        print("[1/3] Building 2025 WFV context...")
        context_2025 = await build_momentum_research_context(
            config,
            args.wfv_start,
            args.wfv_end,
            client,
            print_progress=True,
        )
        print("[2/3] Building 2026 Q1 holdout context...")
        holdout_context = await build_momentum_research_context(
            config,
            args.holdout_start,
            args.holdout_end,
            client,
            print_progress=True,
        )

        print("[3/3] Evaluating WFV candidates...")
        rows: list[dict[str, Any]] = []
        for idx, (label, overrides) in enumerate(_leader_candidate_overrides(), start=1):
            print(f"\n  Candidate {idx}/{len(_leader_candidate_overrides())}: {label}")
            strategy = build_momentum_strategy(config, overrides)
            payload = evaluate_momentum_wfv_candidate(
                context_2025,
                strategy,
                train_days=args.train_days,
                test_days=args.test_days,
                step_days=args.step_days,
                holdout_context=holdout_context,
            )
            row = _condensed_row(label, payload)
            rows.append(row)
            print(
                "    "
                f"WF mean {row['score']['mean_test_return_pct'] or 0:.2f}% | "
                f"WF+ {row['score']['positive_fold_rate_pct'] or 0:.0f}% | "
                f"WFQS {row['score']['wfqs_v2'] or 0:.1f} | "
                f"Q1 {(row['score']['holdout_return_pct'] or 0):.2f}%"
            )

    ranked = _rank_results(rows)

    print("\n=== 2025 WFV Ranking ===")
    for row in ranked:
        score = row["score"]
        print(
            f"{row['rank']:>2}. {row['label']:<18} "
            f"score {score['selection_score']:>6.2f} | "
            f"WF mean {score['mean_test_return_pct'] or 0:>6.2f}% | "
            f"WF+ {score['positive_fold_rate_pct'] or 0:>5.1f}% | "
            f"worst {score['worst_fold_return_pct'] or 0:>6.2f}% | "
            f"Q1 {score['holdout_return_pct'] or 0:>6.2f}%"
        )

    out_dir = Path(args.output_dir)
    out_dir.mkdir(parents=True, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    out_path = out_dir / f"leader_momentum_wfv_{ts}.json"
    out_path.write_text(
        __import__("json").dumps(
            {
                "config": args.config,
                "wfv_period": [args.wfv_start, args.wfv_end],
                "holdout_period": [args.holdout_start, args.holdout_end],
                "train_days": args.train_days,
                "test_days": args.test_days,
                "step_days": args.step_days,
                "rows": ranked,
                "winner_label": ranked[0]["label"] if ranked else None,
                "winner_strategy": ranked[0]["strategy"] if ranked else None,
            },
            indent=2,
            ensure_ascii=True,
            default=str,
        )
    )
    print(f"\nSaved WFV report to: {out_path}")


def main() -> None:
    asyncio.run(main_async())


if __name__ == "__main__":
    main()
