import datetime as dt

from fxhnt.adapters.persistence.cockpit_models import (
    ForwardSummaryRow,
    StrategyAllocationRow,
    StrategyFundingRow,
)
from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
from fxhnt.application.dashboard_service import DashboardService


def _seed(tmp_path):
    repo = ForwardNavRepo(f"sqlite:///{tmp_path/'c.db'}")
    repo.migrate()
    at = dt.datetime(2026, 7, 10)
    def _sum(sid, tr):
        return ForwardSummaryRow(strategy_id=sid, as_of="2026-07-10", days=5, nav=1.0 + tr, total_return=tr,
                                 sharpe=1.0, maxdd=-0.05, gate_status="WAIT", gate_reason="", src_updated_at=at)
    from sqlalchemy.orm import Session
    with Session(repo._engine) as s:
        s.add_all([_sum("bybit_4edge", 0.0031), _sum("bybit_4edge_exec", 0.0019)])
        s.commit()
    repo.replace_allocations([StrategyAllocationRow(strategy_id="bybit_4edge", target_weight=0.54,
        target_dollars=18900.0, policy_version=1, policy_hash="h", as_of="2026-07-10", computed_at=at)], at)
    repo.replace_funding([
        StrategyFundingRow(strategy_id="bybit_4edge", state="funded", label="funded", decay_run=0,
            recovery_run=0, first_pass_date=None, policy_version=2, policy_hash="h",
            as_of="2026-07-10", computed_at=at),
        StrategyFundingRow(strategy_id="multistrat", state="de-funded", label="de-funded", decay_run=0,
            recovery_run=0, first_pass_date="2026-06-20", policy_version=2, policy_hash="h",
            as_of="2026-07-10", computed_at=at),
    ], at)
    return repo


def test_fleet_carries_allocation_funding_and_exec_pairing(tmp_path):
    svc = DashboardService(_seed(tmp_path))
    rows = {f.strategy_id: f for f in svc.fleet()}
    b = rows["bybit_4edge"]
    assert b.alloc_dollars == 18900.0 and abs(b.alloc_weight - 0.54) < 1e-9
    assert b.funding_label == "funded"
    # exec pairing: bybit_4edge modeled paired with bybit_4edge_exec (return 0.19% vs 0.31% -> Δ -0.12%)
    assert b.exec_twin_sid == "bybit_4edge_exec"
    assert abs(b.exec_return_pct - 0.19) < 1e-6
    assert abs(b.exec_divergence_pct - (-0.12)) < 1e-6
    # a de-funded edge
    assert rows["multistrat"].funding_label == "de-funded"
    # the executed twin is flagged for hiding at top level
    assert "bybit_4edge_exec" in svc.exec_twin_sids()


def test_overview_capital_meter(tmp_path):
    ov = DashboardService(_seed(tmp_path)).overview()
    assert ov.gross_deployed == 18900.0
    # ceiling reflects the RESOLVED allocation policy (default settings -> allow_live False -> paper $1M),
    # NOT the old hardcoded $35k — so the meter matches the ceiling the engine actually sizes against.
    assert ov.ceiling == 1_000_000.0
    assert ov.n_funded == 1 and ov.n_defunded == 1


def test_graceful_when_no_alloc_funding(tmp_path):
    # a repo with summaries but NO allocation/funding rows -> fields default, no crash.
    repo = ForwardNavRepo(f"sqlite:///{tmp_path/'e.db'}")
    repo.migrate()
    rows = {f.strategy_id: f for f in DashboardService(repo).fleet()}
    any_row = next(iter(rows.values()))
    assert any_row.alloc_dollars is None and any_row.funding_label == ""


def _seed_multistrat_pair(tmp_path, db_name):
    repo = ForwardNavRepo(f"sqlite:///{tmp_path/db_name}")
    repo.migrate()
    at = dt.datetime(2026, 7, 10)
    def _sum(sid, tr):
        return ForwardSummaryRow(strategy_id=sid, as_of="2026-07-10", days=5, nav=1.0 + tr, total_return=tr,
                                 sharpe=1.0, maxdd=-0.05, gate_status="WAIT", gate_reason="", src_updated_at=at)
    from sqlalchemy.orm import Session
    with Session(repo._engine) as s:
        s.add_all([_sum("multistrat", 0.0031), _sum("multistrat_exec", 0.0019)])
        s.commit()
    return repo, at


def test_detail_exec_venue_from_book_state(tmp_path):
    repo, at = _seed_multistrat_pair(tmp_path, "f.db")
    repo.set_book_state("multistrat_exec_pos", {"positions": {}, "prices": {}, "envelope": 1e6,
                         "venue": "ibkr-paper"}, at)
    svc = DashboardService(repo)
    d = svc.detail("multistrat")
    assert d.exec_twin_sid == "multistrat_exec"
    assert d.exec_venue == "ibkr-paper"


def test_detail_exec_venue_none_when_unrecorded(tmp_path):
    repo, at = _seed_multistrat_pair(tmp_path, "g.db")
    repo.set_book_state("multistrat_exec_pos", {"positions": {}, "prices": {}, "envelope": 1e6}, at)
    svc = DashboardService(repo)
    d = svc.detail("multistrat")
    assert d.exec_twin_sid == "multistrat_exec"
    assert d.exec_venue is None
