"""Forward-validation review: CUSUM edge-decay retires a decaying specialist; a holding edge deploys;
a young one keeps tracking."""
from __future__ import annotations

import datetime as dt

from fxhnt.adapters.persistence import DuckDbAnalyticalStore, SqlStrategyStore
from fxhnt.application.forward import ForwardValidationService
from fxhnt.config import Settings
from fxhnt.domain.factory import RegimeFit, StrategyRecord, StrategyStatus
from fxhnt.domain.models import AssetClass, Market, PriceSeries, StrategySpec

_NOW = dt.datetime(2026, 6, 13, tzinfo=dt.timezone.utc)


class FakeData:
    name = "fake"

    def fetch(self, market: Market, start=None, end=None) -> PriceSeries:  # not used by _review
        import numpy as np
        return PriceSeries(market=market, dates=("0",) * 400, close=np.ones(400))


def _fwd(sid: str, returns: list[float]) -> StrategyRecord:
    return StrategyRecord(
        strategy_id=sid, markets=["NQ"], asset_class=AssetClass.FUTURE,
        spec=StrategySpec(kind="trend", params={"window": 200.0}),
        regime_fits=[RegimeFit(regime="trend_up|low_vol", dsr=0.97, is_sharpe=1.3, oos_sharpe=0.9, passed=True)],
        status=StrategyStatus.FORWARD, discovered_at=_NOW, updated_at=_NOW,
        forward_days=len(returns), forward_sum=sum(returns), forward_sumsq=sum(r * r for r in returns),
        forward_returns=returns,
    )


def test_forward_review_cusum_gate(tmp_path) -> None:
    settings = Settings(operational_dsn=f"sqlite:///{tmp_path / 's.db'}", analytical_path=str(tmp_path / "a.duckdb"))
    store = SqlStrategyStore(settings.operational_dsn)
    svc = ForwardValidationService(FakeData(), DuckDbAnalyticalStore(settings.analytical_path), store, settings,
                                   min_forward_days=15)

    decayed = _fwd("DEC", [0.01] * 10 + [-0.05] * 20)   # clear structural down-shift -> CUSUM negative break
    good = _fwd("GOOD", [0.01] * 30)                     # positive, no negative break
    young = _fwd("YOUNG", [0.002] * 5)                   # < warmup and < min_days
    for rec in (decayed, good, young):
        store.upsert(rec)

    svc._review()  # noqa: SLF001

    assert store.get("DEC").status is StrategyStatus.RETIRED        # edge decayed -> retired by CUSUM
    assert store.get("GOOD").status is StrategyStatus.FORWARD       # edge held -> stays FORWARD (factory_loop promotes via diversification gate)
    assert store.get("YOUNG").status is StrategyStatus.FORWARD      # still tracking
