"""Overlay + corpus lint for the dsh-aris-panel skill corpus.

    python test/test_claude_overlays.py

Covers what a browser smoke test cannot: the 9 Claude reviewer overlays are fresh and complete,
the reviewer-tool drift guard fires, routing tables point only at overlays that exist, retired
variants are referenced nowhere, every SKILL.md has parseable frontmatter, and claude-review
tool names are real.
"""
import importlib.util
import re
import shutil
import tempfile
import unittest
from pathlib import Path

import yaml

ROOT = Path(__file__).resolve().parents[1]
SKILLS = ROOT / "skills"
spec = importlib.util.spec_from_file_location("bco", ROOT / "tools" / "build_claude_overlays.py")
bco = importlib.util.module_from_spec(spec)
spec.loader.exec_module(bco)

DROPPED = ["citation-audit-claude", "paper-claim-audit-claude", "experiment-audit-claude",
           "novelty-check-claude", "meta-apply-claude", "result-to-claim-claude"]
VALID_CLAUDE_TOOLS = {"review", "review_reply", "review_start", "review_reply_start", "review_status"}


def frontmatter(path):
    m = re.match(r"^---\n(.*?)\n---\n", path.read_text(encoding="utf-8").replace("\r\n", "\n"), re.S)
    return yaml.safe_load(m.group(1)) if m else None


class ClaudeOverlays(unittest.TestCase):
    def test_overlays_exist_and_are_fresh(self):
        self.assertEqual(bco.main(["--check"]), 0)
        for base in bco.OVERLAYS:
            fm = frontmatter(SKILLS / f"{base}-claude" / "SKILL.md")
            self.assertEqual(fm["name"], f"{base}-claude")
            self.assertIn("(Claude Code)", fm["description"])
            self.assertIn("mcp__claude-review__review_status", fm["allowed-tools"])
            self.assertNotIn("mcp__codex__codex", fm["allowed-tools"])

    def test_overlay_points_at_an_existing_base(self):
        for base in bco.OVERLAYS:
            body = (SKILLS / f"{base}-claude" / "SKILL.md").read_text(encoding="utf-8")
            self.assertIn(f"../{base}/SKILL.md", body)
            self.assertTrue((SKILLS / base / "SKILL.md").exists(), base)

    def test_drift_guard_rejects_an_unmapped_reviewer_tool(self):
        with tempfile.TemporaryDirectory() as tmp:
            base_root, out = Path(tmp) / "base", Path(tmp) / "out"
            for base in bco.OVERLAYS:
                shutil.copytree(SKILLS / base, base_root / base)
            p = base_root / "research-review" / "SKILL.md"
            p.write_text(p.read_text(encoding="utf-8") + "\nCall `mcp__newreviewer__ask` for a second opinion.\n", encoding="utf-8")
            self.assertEqual(bco.main(["--base-root", str(base_root), "--out", str(out)]), 2)

    def test_workbench_variant_map_matches_overlays(self):
        js = (ROOT / "dsh" / "client.js").read_text(encoding="utf-8")
        block = re.search(r"const CLAUDE_VARIANT_MAP = \{(.*?)\n\}", js, re.S).group(1)
        pairs = dict(re.findall(r"'([\w-]+)':\s*'([\w-]+)'", block))
        self.assertEqual(set(pairs), set(bco.OVERLAYS))
        self.assertTrue(all(v == f"{k}-claude" for k, v in pairs.items()))


class CorpusLint(unittest.TestCase):
    def test_every_skill_has_parseable_frontmatter(self):
        bad = []
        for p in SKILLS.rglob("SKILL.md"):
            try:
                fm = frontmatter(p)
                if not fm or not fm.get("name") or not fm.get("description"):
                    bad.append(str(p.relative_to(SKILLS)))
            except yaml.YAMLError as e:
                bad.append(f"{p.relative_to(SKILLS)}: {e}")
        self.assertEqual(bad, [])

    def test_routing_targets_exist(self):
        for p in SKILLS.glob("*/SKILL.md"):
            for target in re.findall(r"`/([a-z0-9-]+-claude)`", p.read_text(encoding="utf-8")):
                self.assertTrue((SKILLS / target / "SKILL.md").exists(), f"{p.parent.name} routes to missing /{target}")

    def test_retired_variants_are_not_referenced(self):
        rx = re.compile(r"\b(" + "|".join(map(re.escape, DROPPED)) + r")\b")
        hits = [f"{p.relative_to(ROOT)}" for p in [*SKILLS.rglob("*.md"), ROOT / "dsh" / "client.js"]
                if rx.search(p.read_text(encoding="utf-8", errors="replace"))]
        self.assertEqual(hits, [])

    def test_claude_review_tool_names_are_real(self):
        bad = set()
        for p in SKILLS.rglob("*.md"):
            bad |= {t for t in re.findall(r"mcp__claude-review__(\w+)", p.read_text(encoding="utf-8")) if t not in VALID_CLAUDE_TOOLS}
        self.assertEqual(bad, set())


if __name__ == "__main__":
    unittest.main(verbosity=1)
