"""Live per-cell condition assembler — each §2 cell is a genuinely distinct,
isolation-gated circuit, not the plain 3-hop control.

These are fixture-only checks: the assembler builds *plans* (ForwarderPlan targets),
opens no socket, moves no traffic, and stands up no engine. We pin that each cell's
structure encodes its condition, that determinism holds, that federated cells
genuinely span >= 2 houses, and that a non-isolated engine is refused.
"""

import pytest

from cmd_chat.sor import battery
from cmd_chat.sor.assembler import assemble, assemble_all
from cmd_chat.sor.forwarder import ContainmentError


def _cells_by_suffix():
    return {c.cell_id: c for c in battery.enumerate_cells()}


def _assemble_cell(suffix, seed=7):
    cell = next(c for c in battery.enumerate_cells() if c.cell_id.endswith(suffix))
    return assemble(cell, seed)


def test_all_cells_distinct_fingerprints():
    cells = battery.enumerate_cells()
    specs = assemble_all(cells, 0)
    fps = {s.fingerprint() for s in specs.values()}
    assert len(fps) == len(cells) == 6  # every cell is a distinct circuit


def test_same_cell_seed_reproduces_fingerprint():
    cells = battery.enumerate_cells()
    a = assemble_all(cells, 0)
    b = assemble_all(cells, 0)
    assert {k: v.fingerprint() for k, v in a.items()} == {
        k: v.fingerprint() for k, v in b.items()
    }


def test_different_run_index_changes_seed_and_circuit():
    cells = battery.enumerate_cells()
    a = assemble_all(cells, 0)
    b = assemble_all(cells, 1)
    for cid in a:
        assert a[cid].seed != b[cid].seed
        assert a[cid].fingerprint() != b[cid].fingerprint()


def test_all_cells_isolation_gated():
    specs = assemble_all(battery.enumerate_cells(), 0)
    assert all(s.isolation_gated() for s in specs.values())


def test_local_engine_is_refused():
    cell = battery.enumerate_cells()[0]
    with pytest.raises(ContainmentError):
        assemble(cell, 7, engine="local").plans()


def test_rq1_bridge_off_is_plain_single_house():
    spec = _assemble_cell("bridge=off")
    assert spec.bridge_present is False and spec.padding_applied is False
    assert not any(h.is_bridge for h in spec.hops)
    assert spec.span_houses() == 1


def test_rq1_bridge_on_inserts_a_bridge_hop():
    spec = _assemble_cell("bridge=on")
    assert spec.bridge_present is True and spec.padding_applied is False
    assert any(h.is_bridge for h in spec.hops)


def test_rq1_bridge_on_padding_applies_padding():
    spec = _assemble_cell("bridge=on+padding")
    assert spec.bridge_present is True and spec.padding_applied is True
    assert any(h.is_bridge for h in spec.hops)


def test_rq2_bridge_federated_spans_two_houses():
    spec = _assemble_cell("topo=bridge-federated")
    assert spec.bridge_present is True
    assert len({h.house for h in spec.hops if h.house != "bridge"}) >= 2


def test_rq2_directory_federated_spans_two_houses():
    spec = _assemble_cell("topo=directory-federated")
    assert len({h.house for h in spec.hops}) >= 2


def test_assembler_dry_check_all_green(tmp_path):
    summary = battery.assembler_dry_check(tmp_path / "asm")
    assert summary["all_green"] is True
    assert summary["distinct_fingerprints"] is True
    assert summary["all_isolation_gated"] is True
    assert summary["rq1_bridge_arms_live"] is True
    assert summary["rq1_padding_arm_live"] is True
    assert summary["rq2_federation_spans_2plus"] is True


def test_assembler_dry_report_is_write_once(tmp_path):
    out = tmp_path / "asm"
    first = battery.assembler_dry_check(out)
    (out / "assembler-dry.json").read_text()  # exists
    # A second call must not overwrite the immutable report.
    second = battery.assembler_dry_check(out)
    assert first["distinct_fingerprints"] == second["distinct_fingerprints"]
