"""THE HARD-STOP fidelity invariant for the honest-reference core (spec 0a/0d, Task 1).

With the capacity cap non-binding (huge ADV vs AUM) and market-impact off (impact_k=0, no spread injected),
`honest_sleeve_returns` MUST collapse to the plain Σ_symbol weight·return book — i.e. it must equal BOTH
`reconciliation_series` (computed directly off the same injected weights) AND `sleeve_returns_from_store`
(the real sleeve engine's own cost-free GROSS series), day-by-day, to float tolerance. If this ever stops
holding, the whole capacity/cost-honest pipeline built on top of this module is invalid — do NOT weaken this
assertion to make it pass; fix `honest_sleeve_returns` (or the fixture) instead.
"""
from __future__ import annotations

import math

from fxhnt.adapters.warehouse.timescale_feature_store import TimescaleFeatureStore
from fxhnt.application.bybit_book_eval import sleeve_returns_from_store
from fxhnt.application.bybit_paper_weights import (
    reconciliation_series,
    sleeve_weights_and_contributions,
)
from fxhnt.application.honest_reference import capacity_curve, honest_sleeve_returns
from fxhnt.application.nav_stats import nav_backfill_stats

_DAY = 86_400


def test_capacity_cap_bites_only_at_high_aum() -> None:
    wbd = {1: {"THIN": (1.0, 0.10)}}
    turnover = {"THIN": {1: 1_000_000.0}}
    lo = honest_sleeve_returns(wbd, turnover, {}, aum=35_000.0, participation_cap=0.03, impact_k=0.0)
    hi = honest_sleeve_returns(wbd, turnover, {}, aum=10_000_000.0, participation_cap=0.03, impact_k=0.0)
    assert abs(lo[1] - 0.10 * (30_000/35_000)) < 1e-9   # $35k target > 3%*$1M=$30k -> scaled 30/35
    assert hi[1] < lo[1]                                  # capacity destroys the return at scale


def test_missing_adv_is_zero_capacity_not_infinite() -> None:
    wbd = {1: {"NODATA": (1.0, 0.10)}}
    r = honest_sleeve_returns(wbd, turnover_panel={}, spread_bps={}, aum=35_000.0, impact_k=1.0)
    assert r[1] == 0.0        # no ADV -> position dropped -> contributes nothing (not +0.10)


def test_capacity_curve_one_series_per_aum_and_sharpe_non_increasing() -> None:
    """Multi-day, single-symbol fixture where the participation cap (3% ADV) binds increasingly at higher
    AUM, and the days most exposed to clipping are interleaved with the rest — so the honest Sharpe must
    degrade (or at worst tie) as AUM rises across the whole curve. Values below were constructed offline and
    verified to produce a strictly non-increasing Sharpe sequence at these five AUM points; impact_k=0.0
    isolates the effect to the hard participation cap alone."""
    rets = [0.05, -0.01, 0.02, 0.05, 0.05, 0.03, 0.05, 0.02, -0.02, 0.05,
            0.02, 0.03, 0.03, -0.01, 0.02, -0.01, 0.02, -0.01, 0.05, 0.05,
            -0.01, -0.01, 0.03, 0.05, 0.05, -0.01, -0.01, 0.02, 0.05, -0.01]
    advs = [1_000_000, 3_000_000, 3_000_000, 100_000_000, 100_000_000, 1_000_000, 1_000_000, 3_000_000,
            100_000_000, 100_000_000, 100_000_000, 3_000_000, 100_000_000, 1_000_000, 30_000_000,
            10_000_000, 3_000_000, 1_000_000, 30_000_000, 10_000_000, 100_000_000, 1_000_000,
            100_000_000, 30_000_000, 100_000_000, 100_000_000, 100_000_000, 1_000_000, 3_000_000, 1_000_000]
    wbd = {d: {"SYM": (1.0, rets[d - 1])} for d in range(1, len(rets) + 1)}
    turnover = {"SYM": {d: float(advs[d - 1]) for d in range(1, len(advs) + 1)}}

    aums = [35_000, 100_000, 350_000, 1_000_000, 10_000_000]
    curves = capacity_curve(wbd, turnover, {}, aums=aums, participation_cap=0.03, impact_k=0.0)
    assert set(curves) == set(aums)

    sharpes = []
    for aum in aums:
        series = curves[aum]
        nav = [1.0]
        v = 1.0
        for day in sorted(series):
            v *= (1 + series[day])
            nav.append(v)
        sharpes.append(nav_backfill_stats(nav).sharpe)

    for i in range(len(sharpes) - 1):
        assert sharpes[i + 1] <= sharpes[i] + 1e-9, (aums[i], aums[i + 1], sharpes[i], sharpes[i + 1])


def test_no_cap_no_impact_reproduces_gross_reconciliation() -> None:
    wbd = {1: {"AAA": (0.5, 0.02), "BBB": (-0.5, -0.01)},
           2: {"AAA": (0.4, -0.03), "BBB": (-0.6, 0.05)}}
    turnover = {"AAA": {1: 1e9, 2: 1e9}, "BBB": {1: 1e9, 2: 1e9}}
    out = honest_sleeve_returns(wbd, turnover, spread_bps={}, aum=1_000.0,
                                participation_cap=0.03, impact_k=0.0)
    assert abs(out[1] - (0.5*0.02 + -0.5*-0.01)) < 1e-9
    assert abs(out[2] - (0.4*-0.03 + -0.6*0.05)) < 1e-9


def test_impact_scales_and_floors_at_spread() -> None:
    wbd = {1: {"C": (1.0, 0.0)}}                      # flat return -> net == -cost
    turnover = {"C": {1: 1_000_000.0}}
    # size $10k of $1M ADV, no cap, single day so traded == full position:
    # impact = 1*400*sqrt(10000/1e6) = 40 bps -> cost = 0.004
    r = honest_sleeve_returns(wbd, turnover, spread_bps={}, aum=10_000.0, participation_cap=1.0, impact_k=1.0)
    assert abs(r[1] - (-0.004)) < 1e-6
    # spread floor: impact off, wide spread -> cost == spread fraction
    r2 = honest_sleeve_returns(wbd, turnover, spread_bps={"C": 50.0}, aum=10_000.0,
                               participation_cap=1.0, impact_k=0.0)
    assert abs(r2[1] - (-50.0/1e4)) < 1e-9


def _seed_flat_funding(store: TimescaleFeatureStore, *, days: int = 120) -> None:
    """FLAT perp price, spot == perp (zero basis), nonzero cross-sectional funding — the xsfunding sleeve's
    ONLY source of return here is FUNDING. Mirrors the fixture in test_bybit_book_reconciliation.py; xsfunding
    needs no `unlock_events`, keeping this fixture self-contained."""
    funding = {"AAAUSDT": 0.002, "BBBUSDT": 0.002, "CCCUSDT": 0.002,
               "XXXUSDT": -0.002, "YYYUSDT": -0.002, "ZZZUSDT": -0.002}
    for sym, f in funding.items():
        store.write_features(sym, [(d * _DAY, {
            "close": 100.0, "spot_close": 100.0, "funding": f,
            "high": 100.0, "low": 100.0}) for d in range(days)])


def test_hard_stop_fidelity_against_real_sleeve_engine() -> None:
    """THE LOAD-BEARING GUARD: with the cap non-binding (ADV = 1e12 >> aum=1.0) and impact off (impact_k=0,
    no spread), honest_sleeve_returns(sleeve_weights_and_contributions(store, 'xsfunding'), ...) must equal
    sleeve_returns_from_store(store, 'xsfunding', cost_bps=0.0) day-by-day within 1e-9."""
    store = TimescaleFeatureStore("sqlite://", table="bybit_features")
    _seed_flat_funding(store)

    weights_by_day = sleeve_weights_and_contributions(store, "xsfunding")
    recon = reconciliation_series(weights_by_day)
    engine_gross = sleeve_returns_from_store(store, "xsfunding", cost_bps=0.0)
    store.close()

    assert weights_by_day, "fixture produced no xsfunding weights — check the seed"

    all_syms = {sym for row in weights_by_day.values() for sym in row}
    turnover_panel = {sym: {day: 1e12 for day in weights_by_day} for sym in all_syms}

    honest = honest_sleeve_returns(weights_by_day, turnover_panel, spread_bps={}, aum=1.0,
                                   participation_cap=0.03, impact_k=0.0)

    common = sorted(set(honest) & set(recon) & set(engine_gross))
    assert len(common) >= int(0.95 * len(recon)), (len(common), len(recon))
    for day in common:
        assert math.isclose(honest[day], recon[day], rel_tol=1e-9, abs_tol=1e-12), (day, honest[day], recon[day])
        assert math.isclose(honest[day], engine_gross[day], rel_tol=1e-9, abs_tol=1e-12), \
            (day, honest[day], engine_gross[day])
