"""Cap-coverage audit: every LIVE-book / position-sizing / allocation callsite that feeds the positioning
sleeve MUST pass positioning_coin_gross_cap (opt-in, fail-safe). Research/eval callsites are intentionally
uncapped and are excluded. If you add a new LIVE path, add its callsite here AND thread the cap."""
import re
from pathlib import Path

_ROOT = Path(__file__).resolve().parents[2] / "src" / "fxhnt"

# (file, function-that-calls-a-seam) that are LIVE and MUST pass the cap. Grow this list when a new live
# path is added — that is the point: it forces the author to thread the cap.
_LIVE_CALLSITES = [
    ("application/bybit_paper_book.py", "combined_symbol_weights"),
    ("application/bybit_paper_book.py", "combined_symbol_weights_and_returns"),
    ("application/bybit_paper_book.py", "derive_and_persist_bybit_paper_book"),
    ("application/bybit_paper_backfill.py", "backfill_bybit_paper_book"),
    ("application/allocation_honest_inputs.py", "honest_allocation_inputs"),
    ("application/bybit_testnet_run.py", "run_bybit_testnet"),
]


def _func_body(text: str, fn: str) -> str:
    # crude but effective: from `def fn(` to the next top-level `def ` / `class ` at column 0. (Anchored on
    # `\n(def |class )` rather than a bare `^\S` — with re.MULTILINE, `^` also matches position 0 of the
    # sliced remainder, which would truncate the body to the signature line alone.)
    m = re.search(rf"^def {re.escape(fn)}\(", text, re.M)
    assert m, f"{fn} not found"
    start = m.start()
    nxt = re.search(r"\n(?:def |class )\S", text[m.end():])
    end = m.end() + nxt.start() + 1 if nxt else len(text)
    return text[start:end]


def test_every_live_positioning_callsite_threads_the_cap():
    # Require the param to appear as a FORWARDED keyword arg (`positioning_coin_gross_cap=` or the seam's
    # `coin_gross_cap=` in bybit_testnet_run's direct-seam calls), not merely SOMEWHERE in the body. A bare
    # substring check would pass a partial regression where the param survives in the signature/docstring but
    # is dropped from the inner seam call — exactly the silent break this guard exists to catch.
    _forwarded = re.compile(r"(?:positioning_)?coin_gross_cap\s*=")
    missing = []
    for rel, fn in _LIVE_CALLSITES:
        body = _func_body((_ROOT / rel).read_text(), fn)
        if not _forwarded.search(body):
            missing.append(f"{rel}::{fn}")
    assert not missing, ("LIVE positioning path(s) not FORWARDING positioning_coin_gross_cap (opt-in, "
                         "fail-safe): " + ", ".join(missing))
