from fxhnt.application.forward_models import (
    CAT_DIVERGENCE,
    CAT_EXECUTION_GAP,
    CAT_IMMATURE,
    CAT_PASS,
    ForwardNavRow,
    ForwardSummary,
)
from fxhnt.application.gate import evaluate_gate
from fxhnt.application.gate_policy_resolve import ResolvedPolicy
from fxhnt.application.reconciliation_gate import BacktestReference, evaluate_reconciliation_gate

_RP = ResolvedPolicy(min_forward_days=3, recon_tolerance_frac=0.5, recon_min_band=0.05,
                     recon_min_corr=0.0, max_gap_days=1, min_days=3, min_total_return=0.0, min_sharpe=0.0)


def _rows(navs, start_day=1):
    # consecutive daily rows 2026-07-0X; ret unused by the band checks here
    return [ForwardNavRow(strategy_id="s", date=f"2026-07-{start_day+i:02d}", ret=0.0, nav=v)
            for i, v in enumerate(navs)]


def _summary(nav, days):
    return ForwardSummary(strategy_id="s", as_of="2026-07-09", days=days, nav=nav,
                          total_return=nav - 1.0, sharpe=0.0, maxdd=0.0)


def test_reconciliation_categories():
    ref = BacktestReference(expected_window_return=0.10, daily_returns=None)
    # building (days < min_forward_days) -> IMMATURE
    assert evaluate_reconciliation_gate(_summary(1.10, 2), _rows([1.05, 1.10]), ref, _RP).category == CAT_IMMATURE
    # no backtest reference -> IMMATURE
    assert evaluate_reconciliation_gate(_summary(1.10, 3), _rows([1.0, 1.05, 1.10]), None, _RP).category == CAT_IMMATURE
    # execution gap (date jump > max_gap_days) -> EXECUTION_GAP
    gap = [ForwardNavRow("s", "2026-07-01", 0.0, 1.0), ForwardNavRow("s", "2026-07-02", 0.0, 1.05),
           ForwardNavRow("s", "2026-07-05", 0.0, 1.10)]
    assert evaluate_reconciliation_gate(_summary(1.10, 3), gap, ref, _RP).category == CAT_EXECUTION_GAP
    # forward far below backtest expectation -> DIVERGENCE
    assert evaluate_reconciliation_gate(
        _summary(0.95, 3), _rows([1.0, 0.98, 0.95]), ref, _RP
    ).category == CAT_DIVERGENCE
    # reconciles -> PASS
    assert evaluate_reconciliation_gate(_summary(1.10, 3), _rows([1.0, 1.05, 1.10]), ref, _RP).category == CAT_PASS


def test_gap_rows_overrides_gap_check_only():
    # M1 regression: `gap_rows` (a clean tail) overrides the gap check ONLY — the historical gap in `rows`
    # still would fail EXECUTION_GAP without it, and the prod call (no gap_rows) is unaffected.
    ref = BacktestReference(expected_window_return=0.10, daily_returns=None)
    rows = [ForwardNavRow("s", "2026-07-01", 0.0, 1.0), ForwardNavRow("s", "2026-07-02", 0.0, 1.05),
            ForwardNavRow("s", "2026-07-05", 0.0, 1.10)]           # date jump 07-02 -> 07-05 = gap
    clean_tail = _rows([1.05, 1.10], start_day=2)                   # 07-02, 07-03: no gap
    assert evaluate_reconciliation_gate(_summary(1.10, 3), rows, ref, _RP).category == CAT_EXECUTION_GAP
    assert evaluate_reconciliation_gate(
        _summary(1.10, 3), rows, ref, _RP, gap_rows=clean_tail
    ).category == CAT_PASS


def test_absolute_gate_categories():
    # evaluate_gate: GO -> PASS, everything else -> IMMATURE (a non-reconciliation edge is never de-funded)
    assert evaluate_gate(_summary(1.10, 5), _RP).category == CAT_PASS
    assert evaluate_gate(_summary(1.0, 1), _RP).category == CAT_IMMATURE          # building -> IMMATURE
    neg = ResolvedPolicy(3, 0.5, 0.05, 0.0, 1, 3, 0.05, 0.0)                       # min_total_return=0.05
    assert evaluate_gate(_summary(1.0, 5), neg).category == CAT_IMMATURE          # NO_GO return floor -> IMMATURE
