"""D2.1 — IbkrAccountRepo: per-run REAL DU-account NAV/positions snapshot (idempotent replace) + strategy-
tagged exec_fills reads. Mirrors ForwardNavRepo's migrate/engine construction, tested against SQLite."""
import datetime as dt

from fxhnt.adapters.persistence.ibkr_account_repo import IbkrAccountRepo


def test_snapshot_roundtrip(tmp_path):
    repo = IbkrAccountRepo(f"sqlite:///{tmp_path}/t.db")
    repo.migrate()
    at = dt.datetime(2026, 7, 14, 14, 35)
    repo.replace_snapshot("2026-07-14", nlv=1_028_594.0, cash=500.0, gross=1_000_000.0,
                          positions={"SPY": 10.0}, at=at)
    assert repo.nav_series() == [("2026-07-14", 1_028_594.0)]
    assert repo.latest_positions() == {"SPY": 10.0}
    # idempotent replace
    repo.replace_snapshot("2026-07-14", nlv=1_029_000.0, cash=1.0, gross=1.0, positions={"SPY": 11.0}, at=at)
    assert repo.nav_series() == [("2026-07-14", 1_029_000.0)]


def test_nav_series_multi_date_ascending(tmp_path):
    repo = IbkrAccountRepo(f"sqlite:///{tmp_path}/t2.db")
    repo.migrate()
    at = dt.datetime(2026, 7, 13, 10, 0)
    repo.replace_snapshot("2026-07-13", nlv=1_000_000.0, cash=0.0, gross=1_000_000.0, positions={}, at=at)
    repo.replace_snapshot("2026-07-14", nlv=1_010_000.0, cash=0.0, gross=1_010_000.0, positions={}, at=at)
    assert repo.nav_series() == [("2026-07-13", 1_000_000.0), ("2026-07-14", 1_010_000.0)]


def test_latest_positions_empty_when_no_snapshot(tmp_path):
    repo = IbkrAccountRepo(f"sqlite:///{tmp_path}/t3.db")
    repo.migrate()
    assert repo.nav_series() == []
    assert repo.latest_positions() == {}


def test_fills_for_reads_strategy_tagged_exec_fills(tmp_path):
    repo = IbkrAccountRepo(f"sqlite:///{tmp_path}/t4.db")
    repo.migrate()
    at = dt.datetime(2026, 7, 14, 15, 0)
    from fxhnt.application.exec_capture import FillDTO
    fills = [FillDTO(symbol="SPY", side="BOT", qty=10.0, price=452.3, fee=1.25, status="Filled",
                     shortfall_bps=0.0)]
    repo.record_fills(strategy_id="multistrat", rebalance_id="2026-07-14", fills=fills, at=at)
    out = repo.fills_for("multistrat", limit=10)
    assert len(out) == 1
    assert out[0].symbol == "SPY" and out[0].strategy_id == "multistrat"
    assert repo.fills_for("other_strategy", limit=10) == []


def test_migrate_is_idempotent_and_adds_strategy_id_to_existing_exec_fills(tmp_path):
    """A pre-existing exec_fills table (created before this task, no strategy_id column) must gain the
    column on migrate without losing data — the guarded ALTER path."""
    dsn = f"sqlite:///{tmp_path}/t5.db"
    from sqlalchemy import create_engine, text
    engine = create_engine(dsn)
    with engine.begin() as c:
        c.execute(text(
            "CREATE TABLE exec_fills (client_id VARCHAR(64) PRIMARY KEY, rebalance_id VARCHAR(48), "
            "symbol VARCHAR(32), side VARCHAR(4), qty FLOAT, price FLOAT, fee FLOAT, status VARCHAR(12), "
            "shortfall_bps FLOAT, at DATETIME)"))
        c.execute(text(
            "INSERT INTO exec_fills VALUES ('c1', 'r1', 'SPY', 'BOT', 1.0, 2.0, 0.0, 'Filled', 0.0, "
            "'2026-07-14 00:00:00')"))
    engine.dispose()

    repo = IbkrAccountRepo(dsn)
    repo.migrate()   # must not raise, and must add strategy_id without dropping the pre-existing row
    assert repo.fills_for("", limit=10)[0].symbol == "SPY"   # pre-existing row defaults to ""

    repo.migrate()   # second migrate is a no-op
