"""Assemble the crypto honest-reference RESEARCH REPORT (spec 9) — one pure function over the analytical
store plus INJECTED spread + unlock data. Writes NOTHING (no cockpit / backtest_summary / recon-gate / prod
tables); the caller (the `honest-reference-report` CLI) is the only thing that touches a real DB, and it is
read-only.

The report ties together every Task-1..7 primitive on ONE consistent basis:
  * per-sleeve CAPACITY CURVE — honest (capacity + slippage) stats over an AUM × impact-k grid;
  * the risk-parity BASE BOOK at the base (aum, k) point, its weights, stats and ex-ante leverage ceiling;
  * the three falsification GATES (G1 min-Sharpe survival, G2 leave-one-out marginal, G3 beats-concentration);
  * candidate EDGE #4 (turnover-pump short) run through the same honest-cost pipeline (the killer is
    pump-day slippage), including its marginal vs the positioning sleeve;
  * a SURVIVORSHIP guard note — the pump/positioning edge is survivorship-inflated unless bybit_features
    carries delisted coins, so the report refuses to silently assume it away.
"""
from __future__ import annotations

from typing import Any

from fxhnt.application.allocation_engine import capacity_ceiling_from_curve
from fxhnt.application.bybit_paper_weights import sleeve_weights_and_contributions
from fxhnt.application.honest_gates import (
    beats_concentration,
    book_returns,
    gate_survives,
    loo_marginal,
    risk_parity_weights,
)
from fxhnt.application.honest_leverage import leverage_ceiling
from fxhnt.application.honest_reference import honest_sleeve_returns
from fxhnt.application.nav_stats import nav_backfill_stats
from fxhnt.application.pump_detection import evaluate_pump_edge, pump_signal

_SURVIVORSHIP_NOTE = (
    "verify bybit_features is survivorship-free — delisted coins must be present or the "
    "pump/positioning edge is survivorship-inflated (spec 10)"
)


def _pump_weights(
    sig: dict[int, dict[str, float]],
    close_panel: dict[str, dict[int, float]],
) -> dict[int, dict[str, tuple[float, float]]]:
    """Pair each pump-short signal with its RAW next-day price return (the repo convention: (weight,
    raw_price_return)). honest_sleeve_returns books gross = w * r, so a short (weight -1) on a coin that
    CRASHES (raw_ret = -0.20) correctly books (-1.0) * (-0.20) = +0.20. Do NOT pre-negate the return here."""
    out: dict[int, dict[str, tuple[float, float]]] = {}
    for day, coins in sig.items():
        for coin in coins:
            series_c = close_panel.get(coin)
            if not series_c or day not in series_c:
                continue
            future = [d for d in series_c if d > day]
            if not future:
                continue                                 # no next-day close -> can't realize the short
            next_day = min(future)
            raw_ret = series_c[next_day] / series_c[day] - 1.0
            out.setdefault(day, {})[coin] = (-1.0, raw_ret)
    return out


def _stats(series: dict[int, float]) -> dict[str, Any]:
    """Compounded-NAV summary of a {day: daily_return} series via nav_backfill_stats (the repo convention)."""
    nav = [1.0]
    v = 1.0
    for d in sorted(series):
        v *= (1.0 + series[d])
        nav.append(v)
    s = nav_backfill_stats(nav)
    return {"days": len(series), "sharpe": s.sharpe,
            "total_return_pct": s.total_return_pct, "maxdd_pct": s.max_dd_pct}


def build_honest_report(store, spread_bps, unlock_events, *, sleeves, aums, ks,
                         capacity_min_sharpe: float = 1.0) -> dict:
    """PURE research report over `store` + INJECTED `spread_bps`/`unlock_events`. See module docstring.

    `store` supplies read methods only (`sleeve_weights_and_contributions` via its edge runners,
    `read_panel`, `crypto_close_panel`); NOTHING is written. `sleeves` are Bybit book sleeves
    ("xsfunding" / "unlock" / "positioning"), `aums` a grid of dollar AUMs, `ks` a grid of impact multipliers.
    The base (aum, k) = (aums[0], ks[0]) is where the book, gates and edge-4 marginal are evaluated."""
    aums = list(aums)
    ks = list(ks)
    base_aum, base_k = aums[0], ks[0]

    sleeves_out: dict[str, Any] = {}
    base_series_by_sleeve: dict[str, dict[int, float]] = {}
    all_symbols: set[str] = set()

    for sleeve in sleeves:
        wbd = sleeve_weights_and_contributions(store, sleeve, unlock_events=unlock_events)
        symbols = {sym for row in wbd.values() for sym in row}
        all_symbols |= symbols
        turnover = store.read_panel(sorted(symbols), "turnover")

        curve: dict[Any, dict[Any, dict[str, Any]]] = {}
        for aum in aums:
            curve[aum] = {}
            for k in ks:
                series = honest_sleeve_returns(wbd, turnover, spread_bps, aum=aum, impact_k=k)
                curve[aum][k] = _stats(series)
                if aum == base_aum and k == base_k:
                    base_series_by_sleeve[sleeve] = series
        sharpe_curve = {aum: curve[aum][base_k]["sharpe"] for aum in aums}
        ceiling = capacity_ceiling_from_curve(sharpe_curve, capacity_min_sharpe)
        sleeves_out[sleeve] = {"capacity_curve": curve, "capacity_ceiling": ceiling}

    # --- base risk-parity book at (base_aum, base_k) ---------------------------------------------
    returns_by_sleeve = base_series_by_sleeve
    weights = risk_parity_weights(returns_by_sleeve)
    book = book_returns(returns_by_sleeve, weights)
    book_out = {"weights": weights, "stats": _stats(book)}
    lev_ceiling = leverage_ceiling(book)

    gates = {
        "G1_survives": {s: gate_survives(v) for s, v in returns_by_sleeve.items()},
        "G2_loo_marginal": loo_marginal(returns_by_sleeve),
        "G3_beats_concentration": beats_concentration(returns_by_sleeve),
        "G4_anti_reactive": "n/a: no de-risk rule in research scope; ab_beats_nothing() gates any future de-risk vs do-nothing",
    }

    # --- candidate edge #4: turnover-pump short, run through the honest-cost pipeline -------------
    turnover_all = store.read_panel(sorted(all_symbols), "turnover")
    close_panel = store.crypto_close_panel()
    sig = pump_signal(turnover_all)
    pump_wbd = _pump_weights(sig, close_panel)
    edge4 = evaluate_pump_edge(pump_wbd, turnover_all, spread_bps, aum=base_aum, impact_k=base_k,
                               positioning_series=base_series_by_sleeve.get("positioning"))

    survivorship = {"n_coins": len(all_symbols), "note": _SURVIVORSHIP_NOTE}

    return {
        "aums": aums,
        "ks": ks,
        "base": [base_aum, base_k],
        "sleeves": sleeves_out,
        "book": book_out,
        "gates": gates,
        "leverage_ceiling": lev_ceiling,
        "edge4_pump": edge4,
        "survivorship": survivorship,
    }
