"""Integrity guards for CLAUDE.md — prevents template-leak regression
and full-inventory-table re-introduction."""
import re
from pathlib import Path

CLAUDE_MD = Path(__file__).resolve().parents[1] / "CLAUDE.md"

BANNED_TOP_LEVEL_HEADERS = [
    "## Faculty Preferences",
    "## Design Decisions",
    "## Current State",
    "## Build History",
    "## Notes",
    "## Pending",
]


def _read_outside_code_fences(text: str) -> str:
    """Return CLAUDE.md content with fenced code blocks stripped, so
    matches inside markdown examples don't trigger false positives."""
    return re.sub(r"```.*?```", "", text, flags=re.DOTALL)


def test_no_course_memory_template_leak():
    body = _read_outside_code_fences(CLAUDE_MD.read_text(encoding="utf-8"))
    for header in BANNED_TOP_LEVEL_HEADERS:
        # match at line start
        assert not re.search(rf"(?m)^{re.escape(header)}\s*$", body), (
            f"CLAUDE.md contains course-memory template section {header!r} at "
            f"top level. This belongs in courses/{{id}}/course-memory.md, not "
            f"the plugin CLAUDE.md. See §CLAUDE.md Write Rules rule 2."
        )


def test_skills_reference_not_a_full_table():
    """The Skills Reference section must be a pointer, not a 40-row master
    table. Cap the section's line count between the §Skills Reference header
    and the next H2."""
    text = CLAUDE_MD.read_text(encoding="utf-8")
    match = re.search(r"(?m)^## Skills Reference\s*$", text)
    assert match, "CLAUDE.md missing §Skills Reference header"
    after = text[match.end():]
    next_h2 = re.search(r"(?m)^## ", after)
    section_body = after[: next_h2.start() if next_h2 else len(after)]
    line_count = section_body.strip().count("\n") + 1
    assert line_count <= 12, (
        f"§Skills Reference is {line_count} lines — too long. It should be a "
        f"pointer to skills/*/SKILL.md, not an inline 40-row table. See "
        f"§CLAUDE.md Write Rules rule 1."
    )


def test_python_scripts_not_a_full_table():
    text = CLAUDE_MD.read_text(encoding="utf-8")
    match = re.search(r"(?m)^## Python Scripts\s*$", text)
    assert match, "CLAUDE.md missing §Python Scripts header"
    after = text[match.end():]
    next_h2 = re.search(r"(?m)^## ", after)
    section_body = after[: next_h2.start() if next_h2 else len(after)]
    line_count = section_body.strip().count("\n") + 1
    assert line_count <= 15, (
        f"§Python Scripts is {line_count} lines — too long. Should be a "
        f"pointer to scripts/ + critical-entries list. See §CLAUDE.md Write "
        f"Rules rule 1."
    )


def test_setup_gate_preserved():
    """§Setup Gate is a procedural contract — should not regress."""
    raw = CLAUDE_MD.read_text(encoding="utf-8")
    # Strip code fences so fake ## headers inside template examples don't
    # truncate section_body prematurely.
    text = _read_outside_code_fences(raw)
    match = re.search(r"(?m)^## Setup Gate\s*$", text)
    assert match, "CLAUDE.md missing §Setup Gate — procedural contract dropped!"
    after = text[match.end():]
    next_h2 = re.search(r"(?m)^## ", after)
    section_body = after[: next_h2.start() if next_h2 else len(after)]
    # Should have all 3 numbered checks (headers use ### Check N: format)
    for check in ["Check 1:", "Check 2:", "Check 3:"]:
        assert check in section_body, (
            f"§Setup Gate missing {check!r}. Procedural contract regression. "
            f"See §CLAUDE.md Write Rules rule 3."
        )
