from __future__ import annotations

from apps.intraday_bt.sweep import SweepConfig, load_sweep_config, run_sweep
from libs.intraday.domain import IntradayConfig, ORBStrategyParams


def test_run_sweep_passes_orb_sector_and_overlay_context(monkeypatch) -> None:
    captured: dict[str, object] = {}

    def fake_run_orb_simulation(
        all_intraday,
        trading_days,
        params,
        enrichment,
        ticker_sectors=None,
        vix_by_day=None,
        overlay_tickers_per_day=None,
    ):
        captured["ticker_sectors"] = ticker_sectors
        captured["overlay_tickers_per_day"] = overlay_tickers_per_day
        return []

    monkeypatch.setattr("libs.intraday.orb_simulator.run_orb_simulation", fake_run_orb_simulation)

    config = IntradayConfig(
        strategy_mode="orb",
        orb_strategy=ORBStrategyParams(),
    )
    sweep = SweepConfig(base_config=config, sweep_params={"weight_event_catalyst": [0.12]})

    run_sweep(
        sweep,
        all_intraday={},
        trading_days=["2026-04-21"],
        enrichment={},
        ticker_sectors={"MSTR": "Financial Services"},
        overlay_tickers_per_day={"2026-04-21": {"MSTR"}},
    )

    assert captured["ticker_sectors"] == {"MSTR": "Financial Services"}
    assert captured["overlay_tickers_per_day"] == {"2026-04-21": {"MSTR"}}


def test_run_sweep_uses_combo_specific_orb_context(monkeypatch) -> None:
    seen: list[tuple[int, tuple[str, ...], str, dict[str, set[str]] | None]] = []

    def fake_run_orb_simulation(
        all_intraday,
        trading_days,
        params,
        enrichment,
        ticker_sectors=None,
        vix_by_day=None,
        overlay_tickers_per_day=None,
    ):
        seen.append(
            (
                int(params.prior_event_lookback_days),
                tuple(params.prior_event_types),
                enrichment["marker"],
                overlay_tickers_per_day,
            )
        )
        return []

    monkeypatch.setattr("libs.intraday.orb_simulator.run_orb_simulation", fake_run_orb_simulation)

    config = IntradayConfig(
        strategy_mode="orb",
        orb_strategy=ORBStrategyParams(
            weight_event_catalyst=0.12,
            prior_event_lookback_days=7,
            prior_event_types=["earnings_release", "guidance_update"],
        ),
    )
    sweep = SweepConfig(
        base_config=config,
        sweep_params={
            "prior_event_lookback_days": [5, 7],
            "prior_event_types": [
                ["earnings_release"],
                ["earnings_release", "guidance_update"],
            ],
        },
    )

    def orb_context_resolver(combo_config: IntradayConfig):
        params = combo_config.orb_strategy or ORBStrategyParams()
        marker = f"{params.prior_event_lookback_days}:{','.join(params.prior_event_types)}"
        return (
            {"marker": marker},
            None,
            {"2026-04-21": {marker}},
        )

    run_sweep(
        sweep,
        all_intraday={},
        trading_days=["2026-04-21"],
        enrichment={"marker": "base"},
        orb_context_resolver=orb_context_resolver,
    )

    assert seen == [
        (5, ("earnings_release",), "5:earnings_release", {"2026-04-21": {"5:earnings_release"}}),
        (
            5,
            ("earnings_release", "guidance_update"),
            "5:earnings_release,guidance_update",
            {"2026-04-21": {"5:earnings_release,guidance_update"}},
        ),
        (7, ("earnings_release",), "7:earnings_release", {"2026-04-21": {"7:earnings_release"}}),
        (
            7,
            ("earnings_release", "guidance_update"),
            "7:earnings_release,guidance_update",
            {"2026-04-21": {"7:earnings_release,guidance_update"}},
        ),
    ]


def test_run_sweep_filters_intraday_to_combo_candidate_map(monkeypatch) -> None:
    seen: list[list[str]] = []

    def fake_run_orb_simulation(
        all_intraday,
        trading_days,
        params,
        enrichment,
        ticker_sectors=None,
        vix_by_day=None,
        overlay_tickers_per_day=None,
    ):
        seen.append(sorted(all_intraday.get("2026-04-21", {}).keys()))
        return []

    monkeypatch.setattr("libs.intraday.orb_simulator.run_orb_simulation", fake_run_orb_simulation)

    config = IntradayConfig(
        strategy_mode="orb",
        orb_strategy=ORBStrategyParams(),
    )
    sweep = SweepConfig(
        base_config=config,
        sweep_params={"min_price": [10.0, 15.0]},
    )

    def orb_context_resolver(combo_config: IntradayConfig):
        params = combo_config.orb_strategy or ORBStrategyParams()
        if params.min_price >= 15.0:
            return {}, None, None, {"2026-04-21": ["BBB", "SPY"]}
        return {}, None, None, {"2026-04-21": ["AAA"]}

    run_sweep(
        sweep,
        all_intraday={"2026-04-21": {"AAA": [{}], "BBB": [{}], "SPY": [{}]}},
        trading_days=["2026-04-21"],
        enrichment={},
        orb_context_resolver=orb_context_resolver,
    )

    assert seen == [["AAA"], ["BBB", "SPY"]]


def test_load_sweep_config_preserves_literal_none_string_enums(tmp_path) -> None:
    sweep_path = tmp_path / "sweep.yaml"
    sweep_path.write_text(
        "\n".join(
            [
                "sweep:",
                "  vwap_exit_mode:",
                "    - none",
                "    - floor",
                "  spy_intraday_guard_pct:",
                "    - none",
                "    - -0.004",
            ]
        )
    )

    config = IntradayConfig(
        strategy_mode="orb",
        orb_strategy=ORBStrategyParams(),
    )
    sweep = load_sweep_config(str(sweep_path), config)

    assert sweep.sweep_params["vwap_exit_mode"] == ["none", "floor"]
    assert sweep.sweep_params["spy_intraday_guard_pct"] == [None, -0.004]


def test_load_sweep_config_reads_trade_day_objective(tmp_path) -> None:
    sweep_path = tmp_path / "sweep.yaml"
    sweep_path.write_text(
        "\n".join(
            [
                "objective:",
                "  name: trade_day_adjusted",
                "  target_days_with_trades: 80",
                "  trade_day_bonus: 0.5",
                "sweep:",
                "  soft_day_vwap_reclaim_size_scale:",
                "    - 0.05",
                "    - 0.08",
            ]
        )
    )

    config = IntradayConfig(
        strategy_mode="orb",
        orb_strategy=ORBStrategyParams(),
    )
    sweep = load_sweep_config(str(sweep_path), config)

    assert sweep.objective["name"] == "trade_day_adjusted"
    assert sweep.objective["target_days_with_trades"] == 80
    assert sweep.sweep_params["soft_day_vwap_reclaim_size_scale"] == [0.05, 0.08]
