"""SS3 RQ1-P2 loader — the freeze-DERIVED run-index paired bootstrap, on SYNTHETIC
pcaps only (docs/stage-05-rq1p2-pairing-clarification.md).

These pin that the loader pairs the bridge-on (no-pad) and bridge-on+padding arms
**by run index** into one `confirm.PairedCircuit` per index — the only balanced
pairing the R=30 interleaved design supports — and that the frozen §6 decision
function `confirm.rq1_p2_padding` consumes those units unchanged. NO confirmatory
record is read (prereg §2 blinding): run dirs are hand-built with synthetic pcaps
and metrics.json that carry a `run_index`.
"""

import json
import struct

from cmd_chat.sor.analysis import confirm, confirm_load


def _write_series_pcap(path, series):
    """Minimal LINKTYPE_RAW pcap placing one packet of size series[k] at t=k, so
    bin_pcap_bytes(path, bins=len(series)) reproduces `series` bin-for-bin."""
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "wb") as f:
        f.write(struct.pack("<IHHiIII", 0xA1B2C3D4, 2, 4, 0, 0, 65535, 101))
        for k, nbytes in enumerate(series):
            f.write(struct.pack("<IIII", k, 0, nbytes, nbytes))
            f.write(b"x" * nbytes)


def _write_circuit(run_dir, cid, ingress_series, egress_series):
    pdir = run_dir / "circuits" / cid / "pcap"
    _write_series_pcap(pdir / "hop0.pcap", ingress_series)      # ingress
    _write_series_pcap(pdir / "hop2.pcap", egress_series)       # egress (hops=3)


def _write_metrics(run_dir, cell_id, rq, padding, run_index):
    """Like the RQ1-P1 helper but ALSO persists run_index — the pairing key."""
    run_dir.mkdir(parents=True, exist_ok=True)
    (run_dir / "metrics.json").write_text(
        json.dumps({
            "cell_id": cell_id, "rq": rq,
            "padding_applied": padding, "run_index": run_index,
        }),
        encoding="utf-8",
    )


_ON = "RQ1/topo=1house/selector=static/bridge=on"
_PAD = "RQ1/topo=1house/selector=static/bridge=on+padding"


def _write_arm_run(tmp_path, *, run_index, padding):
    """A one-run arm dir: two circuits, each hop0/hop2 sharing that circuit's own
    temporal profile so the diagonal (linked) pearson beats the cross (unlinked).
    The +pad arm is flattened so its measured linkability is lower than no-pad."""
    cell = _PAD if padding else _ON
    rd = tmp_path / f"cell-{'pad' if padding else 'on'}-r{run_index}"
    _write_metrics(rd, cell, "RQ1", padding, run_index)
    if padding:
        # Padding masks the temporal profile → both circuits look flat → low AUC.
        _write_circuit(rd, "c0", [50, 50, 50, 50], [50, 50, 50, 50])
        _write_circuit(rd, "c1", [50, 50, 50, 50], [50, 50, 50, 50])
    else:
        _write_circuit(rd, "c0", [100, 10, 100, 10], [100, 10, 100, 10])
        _write_circuit(rd, "c1", [10, 100, 10, 100], [10, 100, 10, 100])
    return rd


def test_pairs_bridge_on_arms_by_run_index(tmp_path):
    # Three run indices present in BOTH arms → three PairedCircuit units.
    for i in range(3):
        _write_arm_run(tmp_path, run_index=i, padding=False)
        _write_arm_run(tmp_path, run_index=i, padding=True)
    paired = confirm_load.collect_rq1_p2_paired(tmp_path, bins=4, hops=3)
    assert len(paired) == 3
    # Each unit carries both arms' pair sets (2x2 score matrix → 4 pairs each).
    for u in paired:
        assert len(u.nopad_pairs) == 4 and len(u.pad_pairs) == 4


def test_per_run_delta_aucs_are_positive_when_padding_lowers_linkability(tmp_path):
    for i in range(4):
        _write_arm_run(tmp_path, run_index=i, padding=False)
        _write_arm_run(tmp_path, run_index=i, padding=True)
    paired = confirm_load.collect_rq1_p2_paired(tmp_path, bins=4, hops=3)
    deltas = confirm_load.per_run_delta_aucs(paired)
    assert len(deltas) == 4                        # one ΔAUC_i per paired run index
    # no-pad AUC ~1 (linked beats cross), +pad AUC ~0.5 (flat) → ΔAUC_i > 0.
    assert all(d > 0.0 for d in deltas)


def test_feeds_frozen_rq1_p2_padding_decision(tmp_path):
    for i in range(5):
        _write_arm_run(tmp_path, run_index=i, padding=False)
        _write_arm_run(tmp_path, run_index=i, padding=True)
    paired = confirm_load.collect_rq1_p2_paired(tmp_path, bins=4, hops=3)
    test = confirm.rq1_p2_padding(paired, seed=1, n_resamples=200)
    assert test.name == "RQ1-P2" and test.effect == "ΔAUC"
    assert test.ci.point > 0.0 and test.decision == "padding-effective"


def test_graceful_when_a_run_index_missing_an_arm(tmp_path):
    # run_index 0,1 in both arms; run_index 2 only no-pad, run_index 3 only +pad.
    for i in (0, 1):
        _write_arm_run(tmp_path, run_index=i, padding=False)
        _write_arm_run(tmp_path, run_index=i, padding=True)
    _write_arm_run(tmp_path, run_index=2, padding=False)   # unpaired
    _write_arm_run(tmp_path, run_index=3, padding=True)    # unpaired
    paired = confirm_load.collect_rq1_p2_paired(tmp_path, bins=4, hops=3)
    # Only the two indices present in BOTH arms are paired; frozen R is not
    # silently redefined — the unpaired runs are simply dropped from the pairing.
    assert len(paired) == 2
