"""B1/B2 anti-drift gate: the hand-maintained headline surfaces must agree
with the machine truth (rules.json), and FEDERATION.md must stay in pointer
form for the corpus version.

Pins ``scripts/check_headline_coherence.py`` — the checker invoked by
``gen_rules_index.py --check`` — with a green live-repo run plus deliberate
negative fixtures for every failure class the 2026-07-03 Court review
confirmed in the wild (F-M3/F-M4).
"""

from __future__ import annotations

import importlib.util
import json
import subprocess
import sys
from pathlib import Path

_REPO_ROOT = Path(__file__).resolve().parents[2]
_SCRIPT = _REPO_ROOT / "scripts" / "check_headline_coherence.py"


def _load_module():
    spec = importlib.util.spec_from_file_location("check_headline_coherence", _SCRIPT)
    assert spec and spec.loader
    mod = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = mod
    spec.loader.exec_module(mod)
    return mod


chc = _load_module()


# ---------------------------------------------------------------------------
# Fixture: a minimal coherent repo tree (2 MUSTs across 2 families).
# ---------------------------------------------------------------------------


def _write_fixture(
    root: Path,
    *,
    footer: str = "**2 across 2 families**",
    scope_line: str = "the corpus of MUST rules (see rules.json — currently 2 MUSTs across 2 families)",
    family_rows: str = "| AG — Agents | AG-01 | agent discipline |\n| TS — Tests | TS-01 | test discipline |",
    status: str = (
        f"> **Status:** v{chc.CONTRACT_VERSION} of this contract (contract version; "
        "the standards corpus\n> versions independently — see docs/standards/README.md)."
    ),
    corpus_intro: str = "The standards corpus lives at docs/standards/ (see\ndocs/standards/README.md for the current\ncorpus version).",
    extra_federation: str = "",
) -> None:
    std = root / "docs" / "standards"
    std.mkdir(parents=True)
    rules = {
        "schema": 1,
        "counts": {"anchored": 2, "must": 2, "should": 0},
        "rules": [
            {"id": "AG-01", "family": "AG", "doc": "AGENTS.md", "kind": "MUST", "indexed": True},
            {"id": "TS-01", "family": "TS", "doc": "TESTING.md", "kind": "MUST", "indexed": True},
        ],
    }
    (std / "rules.json").write_text(json.dumps(rules), encoding="utf-8")
    (std / "README.md").write_text(
        f"# Standards\n\n## Index of MUSTs\n\n> history … taking the index to {footer}.\n\n"
        "| ID | Source | Rule |\n|---|---|---|\n| AG-01 | AGENTS | x |\n| TS-01 | TESTING | y |\n",
        encoding="utf-8",
    )
    (root / "SCOPE.md").write_text(
        f"# SCOPE\n\n**SP-01 Purpose.** {scope_line}.\n", encoding="utf-8"
    )
    (root / "FEDERATION.md").write_text(
        "# FEDERATION — contract\n\n"
        f"{status}\n\n## §4\n\n{corpus_intro}\n\n"
        "| Standard | Rules | What it covers |\n|---|---|---|\n"
        f"{family_rows}\n\n{extra_federation}\n",
        encoding="utf-8",
    )


class TestGreenPath:
    def test_live_repo_is_coherent(self) -> None:
        # The actual repo must hold the invariant this gate defends.
        assert chc.check(_REPO_ROOT) == []

    def test_script_exits_zero_on_live_repo(self) -> None:
        result = subprocess.run(
            [sys.executable, str(_SCRIPT)], capture_output=True, text=True, check=False
        )
        assert result.returncode == 0, result.stdout + result.stderr
        assert "GREEN" in result.stdout

    def test_fixture_tree_is_green(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path)
        assert chc.check(tmp_path) == []


class TestCountCoherence:
    """B1 — every count surface must equal rules.json."""

    def test_stale_readme_footer_fails(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, footer="**1 across 1 families**")
        errors = chc.check(tmp_path)
        assert any("Index footer" in e for e in errors)

    def test_last_footer_claim_wins(self, tmp_path: Path) -> None:
        # The maintenance history accretes counts; only the LAST is current.
        _write_fixture(tmp_path, footer="**1 across 1 families**. Later → **2 across 2 families**")
        assert chc.check(tmp_path) == []

    def test_stale_scope_count_fails(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, scope_line="the corpus (currently 218 MUSTs across 16 families)")
        errors = chc.check(tmp_path)
        assert any("SCOPE.md" in e and "218" in e for e in errors)

    def test_missing_family_row_fails(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, family_rows="| AG — Agents | AG-01 | agent discipline |")
        errors = chc.check(tmp_path)
        assert any("family table has 1" in e for e in errors)


class TestVersionCoherence:
    """B2 — FEDERATION.md: pointer form only; one owner for the contract version."""

    def test_at_time_of_writing_is_banned(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, extra_federation="The corpus (v2.10 at time of writing).")
        errors = chc.check(tmp_path)
        assert any("at time of writing" in e for e in errors)

    def test_bare_corpus_version_literal_fails(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, extra_federation="The corpus is at v2.10 today.")
        errors = chc.check(tmp_path)
        assert any("literal corpus-space version" in e and "v2.10" in e for e in errors)

    def test_rule_citation_versions_are_history_not_claims(self, tmp_path: Path) -> None:
        _write_fixture(
            tmp_path,
            extra_federation="baseline (DOC-01 v2.11, 2026-05-28); DOC-19 (MUST since v2.21).",
        )
        assert chc.check(tmp_path) == []

    def test_library_and_contract_versions_are_out_of_scope(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, extra_federation="flags since v0.35.0; pin v1.0 windows.")
        assert chc.check(tmp_path) == []

    def test_missing_corpus_pointer_fails(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, corpus_intro="The standards corpus lives at docs/standards/.")
        errors = chc.check(tmp_path)
        assert any("corpus-version pointer" in e for e in errors)

    def test_contract_version_must_match_the_constant(self, tmp_path: Path) -> None:
        _write_fixture(tmp_path, status="> **Status:** v9.9 of this contract.")
        errors = chc.check(tmp_path)
        assert any("CONTRACT_VERSION" in e for e in errors)


class TestGateIntegration:
    def test_gen_rules_index_check_carries_the_coherence_gate(self) -> None:
        # gen_rules_index --check must include the coherence verdict, so the
        # existing CI channel (test_rules_index.py) defends these surfaces too.
        result = subprocess.run(
            [sys.executable, str(_REPO_ROOT / "scripts" / "gen_rules_index.py"), "--check"],
            capture_output=True,
            text=True,
            check=False,
        )
        assert result.returncode == 0, result.stdout + result.stderr
        assert "coherence GREEN" in result.stdout
