import datetime as dt

from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
from fxhnt.application.forward_migration import migrate_track


def _repo(tmp_path):
    repo = ForwardNavRepo(f"sqlite:///{tmp_path}/mig.db")
    repo.migrate()
    return repo


class _RecomputeStrat:
    def __init__(self, series):
        self._series = series

    def advance(self, last_date, extra):
        return list(self._series), extra


def _reg(monkeypatch, sid, mode):
    from fxhnt import registry
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, sid,
                        {"display_name": "T", "sleeve": "x", "gate_spec": {},
                         "definition": {"params": {"v": 1}, "version": 1, "record_mode": mode}})


def test_recompute_migration_preserves_t0_on_match(tmp_path, monkeypatch):
    repo = _repo(tmp_path)
    _reg(monkeypatch, "t_rec", "recompute")
    series = [("2026-07-06", 0.01), ("2026-07-07", 0.02)]
    # accumulated state whose nav MATCHES the recompute over [inception, ...]
    nav = 1.0
    days = []
    for d, r in series:
        nav *= 1 + r
        days.append({"date": d, "ret": r, "nav": nav})
    state = {"inception": "2026-07-06", "last_date": "2026-07-07", "nav": nav, "days": days, "extra": {}}
    out = migrate_track(repo, "t_rec", state, _RecomputeStrat(series), today="2026-07-07",
                        at=dt.datetime(2026, 7, 7, 23, 30))
    assert out["status"] == "match" and repo.active_anchor("t_rec").t0 == "2026-07-06"


def test_recompute_migration_flags_divergence(tmp_path, monkeypatch):
    repo = _repo(tmp_path)
    _reg(monkeypatch, "t_rec", "recompute")
    series = [("2026-07-06", 0.01), ("2026-07-07", 0.02)]
    # accumulated state that DRIFTED (wrong nav) → divergence flagged
    state = {"inception": "2026-07-06", "last_date": "2026-07-07", "nav": 5.0,
             "days": [{"date": "2026-07-06", "ret": 0.5, "nav": 1.5},
                      {"date": "2026-07-07", "ret": 2.0, "nav": 4.5}], "extra": {}}
    out = migrate_track(repo, "t_rec", state, _RecomputeStrat(series), today="2026-07-07",
                        at=dt.datetime(2026, 7, 7, 23, 30))
    assert out["status"] == "diverged" and out["max_abs_nav_diff"] > 0.02


def test_recompute_missing_dates_flags_divergence(tmp_path, monkeypatch):
    repo = _repo(tmp_path)
    _reg(monkeypatch, "t_rec", "recompute")
    state = {"inception": "2026-07-06", "last_date": "2026-07-07", "nav": 100.0,
             "days": [{"date": "2026-07-06", "ret": 9.0, "nav": 10.0},
                      {"date": "2026-07-07", "ret": 9.0, "nav": 100.0}], "extra": {}}
    out = migrate_track(repo, "t_rec", state, _RecomputeStrat([]), today="2026-07-07",
                        at=dt.datetime(2026, 7, 7, 23, 30))
    assert out["status"] == "diverged" and out["missing_dates"] == 2


def test_recompute_window_bounded_by_today(tmp_path, monkeypatch):
    repo = _repo(tmp_path)
    _reg(monkeypatch, "t_rec", "recompute")
    series = [("2026-07-06", 0.01), ("2026-07-07", 0.02), ("2026-07-09", 0.5)]  # 07-09 is beyond today
    nav = 1.0
    days = []
    for d, r in series[:2]:
        nav *= 1 + r
        days.append({"date": d, "ret": r, "nav": nav})
    state = {"inception": "2026-07-06", "last_date": "2026-07-07", "nav": nav, "days": days, "extra": {}}
    out = migrate_track(repo, "t_rec", state, _RecomputeStrat(series), today="2026-07-07",
                        at=dt.datetime(2026, 7, 7, 23, 30))
    assert out["status"] == "match"   # the future 07-09 row is excluded from the reconcile window


def test_observed_migration_imports_verbatim(tmp_path, monkeypatch):
    repo = _repo(tmp_path)
    _reg(monkeypatch, "t_obs", "observed")
    state = {"inception": "2026-07-06", "last_date": "2026-07-07", "nav": 1.03,
             "days": [{"date": "2026-07-06", "ret": 0.01}, {"date": "2026-07-07", "ret": 0.02}],
             "extra": {"positions": {"BTCUSDT": 0.5}}}
    out = migrate_track(repo, "t_obs", state, _RecomputeStrat([]), today="2026-07-07",
                        at=dt.datetime(2026, 7, 7, 23, 30))
    assert out["status"] == "imported" and out["n"] == 2
    assert repo.record_series("t_obs") == [("2026-07-06", 0.01), ("2026-07-07", 0.02)]
    assert repo.get_book_state("t_obs") == {"positions": {"BTCUSDT": 0.5}}


def test_recompute_track_migration_seeds_anchor_when_forward_summary_exists_but_no_anchor(tmp_path):
    """CRITICAL 1 regression (general, checked on a recompute track): production may have a pre-branch
    forward_summary row (the old ingest wrote all tracks) but no anchor row. If a recompute track has no
    entry in `track_builders`, `migrate-forward-anchors` skips it and the anchor is never seeded — the next
    engine run's `active_anchor(sid)` is None while `has_forward_history(sid)` is True, tripping the
    lost-anchor guard in `forward_engine.run_track` and crashing that track's nightly asset. Assert the
    recompute track is in the builders map, and that running `migrate_track` on the crash precondition
    (forward_summary row present, no anchor) seeds an anchor — so a subsequent engine run will not raise."""
    from fxhnt.adapters.orchestration.migration_builders import track_builders
    from fxhnt.application.forward_models import ForwardSummary
    from fxhnt.config import get_settings

    repo = _repo(tmp_path)
    sid = "sixtyforty"
    assert sid in track_builders(get_settings(), str(tmp_path))   # the CRITICAL-1 fix: recompute track has a builder

    # seed the pre-branch forward_summary row (old ingest wrote all tracks) — no anchor exists yet.
    repo.upsert_summary(
        ForwardSummary(strategy_id=sid, as_of="2026-07-06", days=10, nav=1.05,
                       total_return=0.05, sharpe=1.0, maxdd=-0.02),
        gate_status="WAIT", gate_reason="pre-branch", at=dt.datetime(2026, 7, 6, 23, 30))
    assert repo.has_forward_history(sid) is True
    assert repo.active_anchor(sid) is None   # the exact crash precondition

    series = [("2026-07-06", 0.01), ("2026-07-07", 0.02)]
    state = {"inception": "2026-07-06", "last_date": "2026-07-07", "nav": 1.03,
             "days": [{"date": "2026-07-06", "ret": 0.01, "nav": 1.01},
                      {"date": "2026-07-07", "ret": 0.02, "nav": 1.03}], "extra": {}}
    migrate_track(repo, sid, state, _RecomputeStrat(series), today="2026-07-07",
                  at=dt.datetime(2026, 7, 7, 23, 30))
    # anchor now exists — a subsequent engine run's active_anchor(sid) is no longer None, so the
    # lost-anchor guard (active is None and has_forward_history) will not trip.
    assert repo.active_anchor(sid) is not None
