"""RQ1/RQ2 confirmatory gates — validated on synthetic ground truth only.

Each test constructs a distribution whose §6 verdict is known by construction
(a clear leak / a null; padding that works / doesn't; federation that grows /
shrinks / is flat; a funnelling mechanism) and asserts the frozen gate fires the
right way. No confirmatory-cell data is involved. The Holm test pins that the
reported RQ1/RQ2 subset is corrected against the full frozen family of 7.
"""

from cmd_chat.sor.analysis.confirm import (
    FROZEN_FAMILY_SIZE,
    PairedCircuit,
    apply_holm,
    rq1_p1_leak,
    rq1_p2_padding,
    rq2_p1_delta_h,
    rq2_p3_funnel,
)


class _LCG:
    def __init__(self, seed=0x2468ACE0):
        self.s = seed
    def u(self):
        self.s = (1103515245 * self.s + 12345) & 0x7FFFFFFF
        return self.s / 0x7FFFFFFF


def _pairs(hi_mean, lo_mean, n=80, rng=None):
    """n linked pairs near hi_mean + n unlinked near lo_mean, with jitter."""
    rng = rng or _LCG()
    out = []
    for _ in range(n):
        out.append((hi_mean + 0.1 * rng.u(), True))
        out.append((lo_mean + 0.1 * rng.u(), False))
    return out


# --------------------------------------------------------------------------- #
# RQ1-P1 — leak gate + materiality label.
# --------------------------------------------------------------------------- #
def test_rq1p1_material_leak():
    t = rq1_p1_leak(_pairs(0.9, 0.1), seed=1, n_resamples=2000)
    assert t.decision == "leak"
    assert t.label == "material"       # CI lower bound >= 0.60
    assert t.ci.excludes(0.5)


def test_rq1p1_null_when_scores_overlap():
    # Linked and unlinked drawn from the same band -> AUC ~ 0.5, CI spans it.
    t = rq1_p1_leak(_pairs(0.5, 0.5), seed=1, n_resamples=2000)
    assert t.decision == "null"
    assert not t.ci.excludes(0.5)


# --------------------------------------------------------------------------- #
# RQ1-P2 — padding efficacy (paired ΔAUC).
# --------------------------------------------------------------------------- #
def test_rq1p2_padding_effective_when_pad_lowers_auc():
    rng = _LCG(0x1111)
    circuits = []
    for _ in range(30):
        nopad = tuple(_pairs(0.9, 0.1, n=4, rng=rng))   # strong linkage
        pad = tuple(_pairs(0.5, 0.5, n=4, rng=rng))      # padding blurs it (AUC~0.5)
        circuits.append(PairedCircuit(nopad, pad))
    t = rq1_p2_padding(circuits, seed=2, n_resamples=2000)
    assert t.decision == "padding-effective"
    assert t.ci.strictly_greater(0.0)


def test_rq1p2_padding_ineffective_when_no_change():
    rng = _LCG(0x2222)
    circuits = []
    for _ in range(30):
        nopad = tuple(_pairs(0.7, 0.3, n=4, rng=rng))
        pad = tuple(_pairs(0.7, 0.3, n=4, rng=rng))      # identical regime
        circuits.append(PairedCircuit(nopad, pad))
    t = rq1_p2_padding(circuits, seed=2, n_resamples=2000)
    assert t.decision == "padding-ineffective"
    assert not t.ci.strictly_greater(0.0)


# --------------------------------------------------------------------------- #
# RQ2-P1 — ΔH two-sided (grow / shrink / inconclusive).
# --------------------------------------------------------------------------- #
def _uniform_circuits(n_circuits, n_senders, per=50):
    return [[per] * n_senders for _ in range(n_circuits)]


def _skewed_circuits(n_circuits, n_senders):
    # One dominant sender -> low entropy.
    return [[1000] + [1] * (n_senders - 1) for _ in range(n_circuits)]


def test_rq2p1_grow_when_federation_is_more_uniform():
    fed = _uniform_circuits(30, 8)         # high H (~3 bits)
    single = _skewed_circuits(30, 8)        # low H
    t = rq2_p1_delta_h(fed, single, seed=3, n_resamples=2000)
    assert t.decision == "grow"
    assert t.ci.strictly_greater(0.0)


def test_rq2p1_honest_shrink_reported():
    fed = _skewed_circuits(30, 8)           # federation funnels -> low H
    single = _uniform_circuits(30, 8)       # matched-N single house, high H
    t = rq2_p1_delta_h(fed, single, seed=3, n_resamples=2000)
    assert t.decision == "shrink"
    assert t.ci.strictly_less(0.0)


def test_rq2p1_inconclusive_when_arms_match():
    fed = _uniform_circuits(30, 8)
    single = _uniform_circuits(30, 8)
    t = rq2_p1_delta_h(fed, single, seed=3, n_resamples=2000)
    assert t.decision == "inconclusive"
    assert not t.ci.excludes(0.0)


# --------------------------------------------------------------------------- #
# RQ2-P3 — funnelling mechanism (Spearman).
# --------------------------------------------------------------------------- #
def test_rq2p3_funnel_negative_rho():
    # Higher top-3 concentration -> lower per-circuit entropy.
    conc = [i / 20.0 for i in range(20)]
    h = [3.0 - c for c in conc]
    t = rq2_p3_funnel(conc, h, seed=4, n_resamples=2000)
    assert t.decision == "funnel"
    assert t.ci.strictly_less(0.0)


# --------------------------------------------------------------------------- #
# Holm over the full frozen family (size 7) while reporting 4.
# --------------------------------------------------------------------------- #
def test_apply_holm_corrects_against_family_of_seven():
    t1 = rq1_p1_leak(_pairs(0.95, 0.05), seed=1, n_resamples=1500)   # tiny p
    t2 = rq1_p2_padding(
        [PairedCircuit(tuple(_pairs(0.9, 0.1, n=4)), tuple(_pairs(0.5, 0.5, n=4)))
         for _ in range(30)], seed=2, n_resamples=1500)
    fed, single = _uniform_circuits(30, 8), _skewed_circuits(30, 8)
    t3 = rq2_p1_delta_h(fed, single, seed=3, n_resamples=1500)
    t4 = rq2_p3_funnel([i / 20.0 for i in range(20)],
                       [3.0 - i / 20.0 for i in range(20)], seed=4, n_resamples=1500)
    holm = apply_holm([t1, t2, t3, t4])
    assert len(holm) == 4
    # Smallest-p test gets the full-family multiplier of 7, not 4.
    top = min(holm, key=lambda h: h.rank)
    assert top.multiplier == FROZEN_FAMILY_SIZE == 7
    mults = sorted(h.multiplier for h in holm)
    assert mults == [4, 5, 6, 7]
