"""DB-first unlock-calendar loader with JSON fallback (back-compat), and an equivalence proof that the
unlock sleeve produces the SAME result from DB-sourced events as from the JSON file. No network."""
import json

from fxhnt.adapters.persistence.unlock_calendar_repo import UnlockCalendarRepo
from fxhnt.application.unlock_calendar_loader import load_unlock_events
from fxhnt.application.unlock_runner import UnlockShortRunner, warehouse_unlock_panel

_EVENTS = [
    {"sym": "ALT", "cliff_day": 150, "n": 5.0e7, "cat": "insiders"},
    {"sym": "BETA", "cliff_day": 200, "n": 8.0e7, "cat": "privateSale"},
]


def _write_json(tmp_path, events):
    p = tmp_path / "unlock_calendar.json"
    p.write_text(json.dumps(events))
    return str(p)


def test_db_first_when_table_populated(tmp_path):
    repo = UnlockCalendarRepo("sqlite://")
    repo.migrate()
    repo.write_events(_EVENTS)
    # JSON has DIFFERENT events — loader must prefer the (populated) DB and ignore the file
    json_path = _write_json(tmp_path, [{"sym": "ZZZ", "cliff_day": 9, "n": 1.0, "cat": "insiders"}])
    got = load_unlock_events(repo=repo, json_path=json_path)
    assert sorted(got, key=lambda e: e["sym"]) == sorted(_EVENTS, key=lambda e: e["sym"])


def test_falls_back_to_json_when_db_empty(tmp_path):
    repo = UnlockCalendarRepo("sqlite://")
    repo.migrate()                          # empty table
    json_path = _write_json(tmp_path, _EVENTS)
    got = load_unlock_events(repo=repo, json_path=json_path)
    assert sorted(got, key=lambda e: e["sym"]) == sorted(_EVENTS, key=lambda e: e["sym"])


def test_falls_back_to_json_when_no_repo(tmp_path):
    json_path = _write_json(tmp_path, _EVENTS)
    got = load_unlock_events(repo=None, json_path=json_path)
    assert sorted(got, key=lambda e: e["sym"]) == sorted(_EVENTS, key=lambda e: e["sym"])


def test_empty_when_neither_source(tmp_path):
    got = load_unlock_events(repo=None, json_path=str(tmp_path / "missing.json"))
    assert got == []


def _close_series(start, n, close=100.0, vol=0.03):
    out = {}
    px = close
    for i in range(n):
        px = px * (1.0 + (vol if i % 2 == 0 else -vol))
        out[start + i] = px
    return out


def test_sleeve_equivalence_db_vs_json(tmp_path):
    """GOLDEN: the unlock sleeve's run() result is byte-identical whether events came from the DB or the
    JSON file — same events in => same calendar => same returns."""
    close_panel = {"ALTUSDT": _close_series(100, 120), "BTCUSDT": _close_series(100, 120)}
    panel = warehouse_unlock_panel(close_panel)

    # JSON-sourced events
    json_path = _write_json(tmp_path, _EVENTS)
    ev_json = load_unlock_events(repo=None, json_path=json_path)

    # DB-sourced events (same input)
    repo = UnlockCalendarRepo("sqlite://")
    repo.migrate()
    repo.write_events(_EVENTS)
    ev_db = load_unlock_events(repo=repo, json_path=str(tmp_path / "missing.json"))

    res_json = UnlockShortRunner(panel, ev_json, pre_window=10, min_shock=0.0).run()
    res_db = UnlockShortRunner(panel, ev_db, pre_window=10, min_shock=0.0).run()

    assert list(res_json.returns) == list(res_db.returns)
    assert res_json.dates == res_db.dates
    assert res_json.n_rebalances == res_db.n_rebalances
