"""Tests for Component 7: assurance-audit skill, references, and routing.

Checks:
- skills/assurance-audit/SKILL.md exists with valid frontmatter (name: assurance-audit)
- Three reference files exist
- SKILL.md references build_inventory, score_course, and the secure/re-lane/declare/liberate framing
- Routing rule present in CLAUDE.md AND AGENTS.md
- SKILL.md contains no em or en dashes
- SKILL.md states separation from course-audit
"""

import re
from pathlib import Path

REPO = Path(__file__).resolve().parents[1]
SKILL_DIR = REPO / "skills" / "assurance-audit"
SKILL_MD = SKILL_DIR / "SKILL.md"
REFS_DIR = SKILL_DIR / "references"
ASSURANCE_MODEL = REFS_DIR / "assurance-model.md"
AUTHENTICITY_FRAMEWORK = REFS_DIR / "authenticity-framework.md"
LEARNING_SCIENCE_LENS = REFS_DIR / "learning-science-lens.md"
CLAUDE_MD = REPO / "CLAUDE.md"
AGENTS_MD = REPO / "AGENTS.md"


# ---------------------------------------------------------------------------
# SKILL.md existence and frontmatter
# ---------------------------------------------------------------------------

def test_skill_md_exists():
    assert SKILL_MD.is_file(), f"skills/assurance-audit/SKILL.md not found at {SKILL_MD}"


def test_skill_md_frontmatter_name():
    text = SKILL_MD.read_text(encoding="utf-8")
    # YAML frontmatter: --- ... --- block at top
    match = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
    assert match, "SKILL.md missing YAML frontmatter (--- block)"
    frontmatter = match.group(1)
    assert "name: assurance-audit" in frontmatter, (
        f"SKILL.md frontmatter missing 'name: assurance-audit'. Got:\n{frontmatter}"
    )


def test_skill_md_has_trigger_phrases():
    """Frontmatter description must include the required trigger phrases."""
    text = SKILL_MD.read_text(encoding="utf-8")
    match = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
    assert match, "SKILL.md missing YAML frontmatter"
    frontmatter = match.group(1)
    required_triggers = [
        "AI-proof",
        "bypass with AI",
        "assurance",
        "AIAS",
        "authentic assessment",
        "certify learning",
    ]
    for trigger in required_triggers:
        assert trigger in frontmatter, (
            f"SKILL.md frontmatter description missing trigger phrase: {trigger!r}"
        )


# ---------------------------------------------------------------------------
# Reference files
# ---------------------------------------------------------------------------

def test_assurance_model_exists():
    assert ASSURANCE_MODEL.is_file(), f"references/assurance-model.md not found at {ASSURANCE_MODEL}"


def test_authenticity_framework_exists():
    assert AUTHENTICITY_FRAMEWORK.is_file(), (
        f"references/authenticity-framework.md not found at {AUTHENTICITY_FRAMEWORK}"
    )


def test_learning_science_lens_exists():
    assert LEARNING_SCIENCE_LENS.is_file(), (
        f"references/learning-science-lens.md not found at {LEARNING_SCIENCE_LENS}"
    )


# ---------------------------------------------------------------------------
# SKILL.md content requirements
# ---------------------------------------------------------------------------

def test_skill_md_references_build_inventory():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "build_inventory" in text, "SKILL.md does not reference build_inventory"


def test_skill_md_references_score_course():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "score_course" in text, "SKILL.md does not reference score_course"


def test_skill_md_secure_re_lane_declare_liberate():
    """SKILL.md must include all four recommendation verbs."""
    text = SKILL_MD.read_text(encoding="utf-8")
    for verb in ("secure", "re-lane", "declare", "liberate"):
        assert verb in text, f"SKILL.md missing recommendation verb: {verb!r}"


def test_skill_md_states_separation_from_course_audit():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "SEPARATE from" in text or "separate from" in text.lower(), (
        "SKILL.md must state that assurance-audit is SEPARATE from course-audit"
    )
    assert "course-audit" in text, "SKILL.md must mention course-audit in the separation statement"


def test_skill_md_no_em_dash():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "—" not in text, "SKILL.md contains an em dash (—)"


def test_skill_md_no_en_dash():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "–" not in text, "SKILL.md contains an en dash (–)"


def test_skill_md_no_html_em_dash():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "&mdash;" not in text, "SKILL.md contains &mdash;"


def test_skill_md_no_html_en_dash():
    text = SKILL_MD.read_text(encoding="utf-8")
    assert "&ndash;" not in text, "SKILL.md contains &ndash;"


# ---------------------------------------------------------------------------
# Routing in CLAUDE.md
# ---------------------------------------------------------------------------

def test_claude_md_has_assurance_routing():
    text = CLAUDE_MD.read_text(encoding="utf-8")
    assert "assurance-audit" in text, "CLAUDE.md missing assurance-audit routing"


def test_claude_md_assurance_routing_mentions_separation():
    text = CLAUDE_MD.read_text(encoding="utf-8")
    # The routing rule must note that assurance-audit is separate from course-audit
    assert "SEPARATE from `course-audit`" in text or "separate from course-audit" in text.lower(), (
        "CLAUDE.md routing rule for assurance-audit must explicitly note separation from course-audit"
    )


def test_claude_md_assurance_trigger_phrases():
    text = CLAUDE_MD.read_text(encoding="utf-8")
    required = ["AI-proof", "bypass with AI", "assurance", "AIAS", "authentic assessment"]
    for phrase in required:
        assert phrase in text, f"CLAUDE.md missing assurance trigger phrase: {phrase!r}"


# ---------------------------------------------------------------------------
# Routing in AGENTS.md
# ---------------------------------------------------------------------------

def test_agents_md_has_assurance_routing():
    text = AGENTS_MD.read_text(encoding="utf-8")
    assert "assurance-audit" in text, "AGENTS.md missing assurance-audit routing"


def test_agents_md_assurance_routing_mentions_separation():
    text = AGENTS_MD.read_text(encoding="utf-8")
    # Must note that assurance-audit is separate from course-audit
    assert "SEPARATE from" in text or "separate from" in text.lower(), (
        "AGENTS.md routing rule for assurance-audit must note separation from course-audit"
    )
    assert "course-audit" in text, "AGENTS.md must mention course-audit in assurance routing"


def test_agents_md_assurance_trigger_phrases():
    text = AGENTS_MD.read_text(encoding="utf-8")
    required = ["AI-proof", "bypass with AI", "assurance", "AIAS", "authentic assessment"]
    for phrase in required:
        assert phrase in text, f"AGENTS.md missing assurance trigger phrase: {phrase!r}"


# ---------------------------------------------------------------------------
# Reference file content checks
# ---------------------------------------------------------------------------

def test_assurance_model_has_aias_levels():
    text = ASSURANCE_MODEL.read_text(encoding="utf-8")
    for level in ("Level 1", "Level 2", "Level 3", "Level 4", "Level 5"):
        assert level in text, f"assurance-model.md missing AIAS {level}"


def test_assurance_model_has_fire():
    text = ASSURANCE_MODEL.read_text(encoding="utf-8")
    assert "FIRE" in text, "assurance-model.md missing FIRE quality test"


def test_assurance_model_has_substitutability():
    text = ASSURANCE_MODEL.read_text(encoding="utf-8")
    assert "Substitutability" in text, "assurance-model.md missing Substitutability test"


def test_assurance_model_has_tier_bands():
    text = ASSURANCE_MODEL.read_text(encoding="utf-8")
    for tier in ("Assured", "Partially Assured", "Unassured", "Counterfeit"):
        assert tier in text, f"assurance-model.md missing tier: {tier!r}"


def test_authenticity_framework_has_5df():
    text = AUTHENTICITY_FRAMEWORK.read_text(encoding="utf-8")
    for dim in ("Real-World Task", "Physical Context", "Social Context", "Assessment Form", "Criteria"):
        assert dim in text, f"authenticity-framework.md missing Gulikers dimension: {dim!r}"


def test_learning_science_lens_is_pointer_heavy():
    text = LEARNING_SCIENCE_LENS.read_text(encoding="utf-8")
    # Must reference the alignment skill and HPI/ID standard
    assert "alignment" in text, "learning-science-lens.md must reference the alignment skill"
    assert "HPI" in text or "instructional-design.md" in text, (
        "learning-science-lens.md must reference HPI/ID standard"
    )


def test_no_upstream_text_verbatim_claim():
    """Reference files must credit adaptation, not verbatim copy."""
    for fpath in (ASSURANCE_MODEL, AUTHENTICITY_FRAMEWORK):
        text = fpath.read_text(encoding="utf-8")
        assert "No upstream text copied verbatim" in text, (
            f"{fpath.name} missing credit line 'No upstream text copied verbatim'"
        )


# ---------------------------------------------------------------------------
# Assurance accuracy v2 authoring guidance (workstream 5)
# ---------------------------------------------------------------------------

class TestDropRuleAndChoiceGroupGuidance:
    def setup_method(self):
        self.text = SKILL_MD.read_text(encoding="utf-8")

    def test_mentions_drop_lowest_and_drop_highest(self):
        assert "drop_lowest" in self.text
        assert "drop_highest" in self.text

    def test_mentions_possible_choice_group_flag(self):
        assert "possible_choice_group" in self.text
        assert "choice_signal" in self.text

    def test_instructs_reporting_post_drop_counts(self):
        assert "counted_after_drops" in self.text or "counted_points_after_drops" in self.text

    def test_warns_against_stating_raw_choice_group_count_as_deliverables(self):
        assert "deliverables" in self.text.lower()

    def test_groups_field_documented_in_inventory_return(self):
        assert "`groups`" in self.text


class TestCourseLevelCalibrationGuidance:
    def setup_method(self):
        self.text = SKILL_MD.read_text(encoding="utf-8")

    def test_references_course_level_module(self):
        assert "course_level.py" in self.text
        assert "detect_level" in self.text
        assert "bloom_expectation" in self.text

    def test_references_level_label_scorecard_field(self):
        assert "level_label" in self.text

    def test_states_low_bloom_not_a_defect_at_intro_level(self):
        assert "not a design gap" in self.text.lower() or "not a design defect" in self.text.lower()


class TestSecuredWordingMatchesReport:
    def setup_method(self):
        self.text = SKILL_MD.read_text(encoding="utf-8")

    def test_peer_review_stated_as_not_counting(self):
        assert '"peer review" does not count' in self.text

    def test_no_bare_peer_review_as_a_detection_signal(self):
        # The pre-v2 phrasing "Keep proctoring and peer-review as detection
        # signals" must be gone -- peer review is not a detection signal.
        assert "peer-review as detection signals" not in self.text


class TestRecommendationAuthoringGuidance:
    """Michael Miller's four recommendation-authoring points (assurance
    accuracy v2, workstream 5)."""

    def setup_method(self):
        self.text = SKILL_MD.read_text(encoding="utf-8")

    def test_grounds_moves_in_actual_items(self):
        assert "generic template move" in self.text

    def test_tilt_pairing_same_sentence_requirement(self):
        assert "SAME sentence" in self.text

    def test_defeated_ai_resistant_moves_named(self):
        lowered = self.text.lower()
        assert "cohort" in lowered or "classmate" in lowered
        assert "screenshot" in lowered

    def test_does_not_recommend_removing_recall_wholesale(self):
        assert "removing recall wholesale" in self.text.lower()


class TestDeferredOpenQuestionsNoted:
    """Out-of-scope items (docs/plan-assurance-accuracy-v2.md) get a one-line
    note, not an implementation: process-evidence-as-securing and motivation
    as a third axis."""

    def setup_method(self):
        self.text = SKILL_MD.read_text(encoding="utf-8")

    def test_deferred_note_present(self):
        assert "Deferred, not implemented" in self.text

    def test_notes_process_evidence_as_securing_is_open(self):
        assert "process evidence" in self.text.lower()
        assert "securing control" in self.text.lower()

    def test_notes_motivation_as_third_axis_is_open(self):
        assert "third report axis" in self.text.lower() or "third axis" in self.text.lower()
