import datetime as dt

from fxhnt import registry
from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
from fxhnt.application.allocation_ingest import record_allocation
from fxhnt.application.allocation_policy import (
    ALLOCATION_POLICY_VERSION,
    allocation_policy_hash,
    resolve_allocation_policy,
)
from fxhnt.application.forward_models import ForwardNavRow as NavRowDTO
from fxhnt.config import get_settings


def test_records_allocation_for_deploy_set(tmp_path, monkeypatch):
    dsn = f"sqlite:///{tmp_path}/ai.db"
    repo = ForwardNavRepo(dsn)
    repo.migrate()
    at = dt.datetime(2026, 7, 9, 23, 30)
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d1",
                        {"display_name": "D1", "sleeve": "x", "gate_spec": {}, "tier": "deploy"})
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d2",
                        {"display_name": "D2", "sleeve": "x", "gate_spec": {}, "tier": "deploy"})
    dates = [(dt.date(2026, 6, 1) + dt.timedelta(days=i)).isoformat() for i in range(60)]  # 60 valid calendar days
    for sid, flip in (("d1", 1), ("d2", 0)):
        for i, dd in enumerate(dates):
            repo.upsert_rows([NavRowDTO(strategy_id=sid, date=dd,
                                        ret=(0.01 if (i % 2) == flip else -0.008), nav=1.0)], at=at)
    out = record_allocation(dsn, equity=20_000.0)
    assert set(out) <= {"d1", "d2"}                              # only deploy-set considered
    assert sum(abs(v) for v in out.values()) <= 35_000.0 + 1e-6  # capped by the default ceiling
    assert repo.current_allocation() == out                     # recorded


def test_dropped_strategy_leaves_no_stale_allocation_row(tmp_path, monkeypatch):
    dsn = f"sqlite:///{tmp_path}/prune.db"
    repo = ForwardNavRepo(dsn)
    repo.migrate()
    at = dt.datetime(2026, 7, 9, 23, 30)
    base = dt.date(2026, 4, 1)
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d1",
                        {"display_name": "D1", "sleeve": "x", "gate_spec": {}, "tier": "deploy"})
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d2",
                        {"display_name": "D2", "sleeve": "x", "gate_spec": {}, "tier": "deploy"})
    for sid, flip in (("d1", 1), ("d2", 0)):
        for i in range(70):
            d = (base + dt.timedelta(days=i)).isoformat()
            repo.upsert_rows([NavRowDTO(strategy_id=sid, date=d,
                                        ret=(0.01 if (i % 2) == flip else -0.008), nav=1.0)], at=at)
    record_allocation(dsn, equity=20_000.0)                     # night 1: both deploy-tier
    # night 2: d2 is no longer deploy-tier (dropped from the deploy set)
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d2",
                        {"display_name": "D2", "sleeve": "x", "gate_spec": {}, "tier": "research"})
    record_allocation(dsn, equity=20_000.0)
    assert "d2" not in repo.current_allocation()                # no stale row for the dropped strategy


def test_record_allocation_uses_resolved_policy_v2(tmp_path, monkeypatch):
    dsn = f"sqlite:///{tmp_path}/resolved.db"
    repo = ForwardNavRepo(dsn)
    repo.migrate()
    at = dt.datetime(2026, 7, 9, 23, 30)
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d1",
                        {"display_name": "D1", "sleeve": "x", "gate_spec": {}, "tier": "deploy"})
    monkeypatch.setitem(registry.STRATEGY_REGISTRY, "d2",
                        {"display_name": "D2", "sleeve": "x", "gate_spec": {}, "tier": "deploy"})
    dates = [(dt.date(2026, 6, 1) + dt.timedelta(days=i)).isoformat() for i in range(60)]
    for sid, flip in (("d1", 1), ("d2", 0)):
        for i, dd in enumerate(dates):
            repo.upsert_rows([NavRowDTO(strategy_id=sid, date=dd,
                                        ret=(0.01 if (i % 2) == flip else -0.008), nav=1.0)], at=at)
    record_allocation(dsn, equity=200_000.0)
    rows = repo.all_allocations()
    assert rows                                                  # allocation was recorded
    settings = get_settings()
    expected_policy = resolve_allocation_policy(settings)
    expected_hash = allocation_policy_hash(expected_policy, ALLOCATION_POLICY_VERSION)
    assert all(r.policy_version == ALLOCATION_POLICY_VERSION for r in rows)   # == 2
    assert all(r.policy_hash == expected_hash for r in rows)
    # default settings -> allow_live False -> paper ceiling ($1M); gross stays well under it here.
    assert sum(abs(r.target_dollars) for r in rows) <= expected_policy.capital_ceiling + 1e-6
