"""Cockpit backtest_summary persistence: ORM table auto-creates via migrate(), upserts are
idempotent (same strategy_id updates in place), and backtest_summaries_from_report maps a
gauntlet report dict -> 3 BacktestSummary DTOs with the right strategy_ids + fields."""
from __future__ import annotations

import datetime as dt

from sqlalchemy import create_engine, inspect

from fxhnt.adapters.persistence.cockpit_models import CockpitBase
from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
from fxhnt.application.backtest_ingest import backtest_summaries_from_report
from fxhnt.application.forward_models import BacktestSummary


def _repo() -> ForwardNavRepo:
    repo = ForwardNavRepo("sqlite://")
    repo.migrate()
    return repo


def _sm(sid: str = "eqfactor_long") -> BacktestSummary:
    return BacktestSummary(
        strategy_id=sid, as_of="2026-05-29", cagr=0.12, ann_vol=0.15, sharpe=0.8,
        max_drawdown=-0.2, passed=True, dsr=0.97, is_sharpe=0.9, oos_sharpe=0.6, pvalue=0.03,
    )


def test_migrate_creates_backtest_summary_table() -> None:
    repo = _repo()
    tables = set(inspect(repo._engine).get_table_names())
    assert "backtest_summary" in tables


def test_backtest_summary_table_in_metadata() -> None:
    eng = create_engine("sqlite://", future=True)
    CockpitBase.metadata.create_all(eng)
    assert "backtest_summary" in set(inspect(eng).get_table_names())


def test_upsert_backtest_summary_and_fetch() -> None:
    repo = _repo()
    repo.upsert_backtest_summary(_sm(), at=dt.datetime(2026, 6, 14))
    got = {r.strategy_id: r for r in repo.all_backtest_summaries()}
    assert "eqfactor_long" in got
    r = got["eqfactor_long"]
    assert r.as_of == "2026-05-29"
    assert r.cagr == 0.12
    assert r.ann_vol == 0.15
    assert r.sharpe == 0.8
    assert r.max_drawdown == -0.2
    assert r.passed is True
    assert r.dsr == 0.97
    assert r.is_sharpe == 0.9
    assert r.oos_sharpe == 0.6
    assert r.pvalue == 0.03


def test_upsert_backtest_summary_is_idempotent() -> None:
    repo = _repo()
    repo.upsert_backtest_summary(_sm(), at=dt.datetime(2026, 6, 14))
    # same strategy_id, updated value -> 1 row, new value
    updated = BacktestSummary(
        strategy_id="eqfactor_long", as_of="2026-06-01", cagr=0.20, ann_vol=0.15, sharpe=1.1,
        max_drawdown=-0.1, passed=False, dsr=0.50, is_sharpe=1.0, oos_sharpe=0.2, pvalue=0.40,
    )
    repo.upsert_backtest_summary(updated, at=dt.datetime(2026, 6, 15))
    rows = repo.all_backtest_summaries()
    assert len(rows) == 1
    assert rows[0].cagr == 0.20
    assert rows[0].passed is False
    assert rows[0].as_of == "2026-06-01"


def _report() -> dict:
    def block(passed: bool) -> dict:
        return {
            "stats": {"cagr": 0.1, "ann_vol": 0.2, "sharpe": 0.5, "max_drawdown": -0.3, "n_obs": 400},
            "verdict": {"passed": passed, "dsr": 0.96, "is_sharpe": 0.7, "oos_sharpe": 0.4,
                        "n_trials": 3, "reasons": [], "pvalue": 0.02},
        }
    return {
        "constructions": {"long": block(True), "ls": block(False), "tilt": block(True)},
        "rebalance_dates": ["2025-01-31", "2025-02-28", "2026-05-29"],
        "n_names_avg": 120.0,
        "settings": {},
    }


def test_backtest_summaries_from_report_maps_3_constructions() -> None:
    sms = backtest_summaries_from_report(_report())
    by_sid = {s.strategy_id: s for s in sms}
    assert set(by_sid) == {"eqfactor_long", "eqfactor_ls", "eqfactor_tilt"}
    # as_of is the LAST rebalance date
    for s in sms:
        assert s.as_of == "2026-05-29"
    # stats fields
    assert by_sid["eqfactor_long"].cagr == 0.1
    assert by_sid["eqfactor_long"].ann_vol == 0.2
    assert by_sid["eqfactor_long"].sharpe == 0.5
    assert by_sid["eqfactor_long"].max_drawdown == -0.3
    # verdict fields
    assert by_sid["eqfactor_long"].passed is True
    assert by_sid["eqfactor_ls"].passed is False
    assert by_sid["eqfactor_long"].dsr == 0.96
    assert by_sid["eqfactor_long"].is_sharpe == 0.7
    assert by_sid["eqfactor_long"].oos_sharpe == 0.4
    assert by_sid["eqfactor_long"].pvalue == 0.02


def test_backtest_summaries_from_report_empty_rebalance_dates() -> None:
    rep = _report()
    rep["rebalance_dates"] = []
    sms = backtest_summaries_from_report(rep)
    assert all(s.as_of == "" for s in sms)
