"""Capacity- and cost-honest daily return series for a Bybit sleeve, from coin-level weights.

Pure + network-free: coin weights (sleeve_weights_and_contributions), the per-coin turnover (ADV) panel,
and the per-coin quoted spread are INJECTED. With the cap non-binding (huge ADV vs AUM) and impact off
(impact_k=0, no spread) this reduces to Σ_symbol weight·ret == reconciliation_series (the fidelity invariant)."""
from __future__ import annotations

import math

IMPACT_COEFF_BPS = 400.0  # market impact (bps) of trading ~100% of a coin's daily ADV in one shot; the
                          # sqrt law scales it down for smaller participation. impact_k is the sensitivity knob.


def honest_sleeve_returns(
    weights_by_day: dict[int, dict[str, tuple[float, float]]],
    turnover_panel: dict[str, dict[int, float]],
    spread_bps: dict[str, float],
    *, aum: float, participation_cap: float = 0.03, impact_k: float = 1.0,
) -> dict[int, float]:
    out: dict[int, float] = {}
    prev: dict[str, float] = {}                       # yesterday's CAPPED weights, for turnover-based slippage
    for day in sorted(weights_by_day):
        row = weights_by_day[day]
        cur: dict[str, float] = {}
        gross = 0.0
        for sym, (w, r) in row.items():
            adv = turnover_panel.get(sym, {}).get(day, 0.0)
            target_notional = abs(w) * aum
            if adv <= 0.0:
                scale = 0.0                            # no ADV data -> zero capacity (honest, not infinite)
            elif target_notional > participation_cap * adv:
                scale = (participation_cap * adv) / target_notional
            else:
                scale = 1.0
            w_capped = w * scale
            cur[sym] = w_capped
            gross += w_capped * r                      # earn on what we HOLD
        cost = 0.0
        for sym in set(cur) | set(prev):               # cost on what we TRADE (entries, rebalances, exits)
            traded_w = abs(cur.get(sym, 0.0) - prev.get(sym, 0.0))
            if traded_w <= 0.0:
                continue
            adv = turnover_panel.get(sym, {}).get(day, 0.0)
            traded_notional = traded_w * aum
            if traded_notional > 0.0 and adv > 0.0:
                impact = impact_k * IMPACT_COEFF_BPS * math.sqrt(traded_notional / adv)   # bps
            else:
                impact = 0.0
            slip_bps = max(impact, spread_bps.get(sym, 0.0))
            cost += traded_w * (slip_bps / 1e4)
        out[day] = gross - cost
        prev = cur
    return out


def capacity_curve(
    weights_by_day: dict[int, dict[str, tuple[float, float]]],
    turnover_panel: dict[str, dict[int, float]],
    spread_bps: dict[str, float],
    *, aums, participation_cap: float = 0.03, impact_k: float = 1.0,
) -> dict[float, dict[int, float]]:
    """One honest return series per AUM (spec 0b). `aums` is an iterable of dollar AUMs; the returned dict
    maps each AUM to honest_sleeve_returns(...) at that AUM. Reveals the capacity curve (Sharpe vs AUM)."""
    return {aum: honest_sleeve_returns(weights_by_day, turnover_panel, spread_bps,
                                        aum=aum, participation_cap=participation_cap, impact_k=impact_k)
            for aum in aums}
