"""Tests for scripts/assessment_design_playbook.py."""
from __future__ import annotations

import sys
from pathlib import Path

import pytest

_scripts_dir = Path(__file__).resolve().parents[1] / "scripts"
if str(_scripts_dir) not in sys.path:
    sys.path.insert(0, str(_scripts_dir))

import assessment_design_playbook as adp


_EXPECTED_CATEGORIES = [
    "Discussions",
    "Quizzes and Exams",
    "Papers and Written Assignments",
    "Projects",
    "Presentations and Oral Assessments",
]


class TestPlaybookContent:
    def setup_method(self):
        self.html = adp.render()

    def test_doctype(self):
        assert self.html.startswith("<!DOCTYPE html")

    def test_title_in_html(self):
        assert "Assessment Design Playbook" in self.html

    def test_no_assessment_accelerator(self):
        assert "Assessment Accelerator" not in self.html

    def test_five_category_headings_present(self):
        for cat in _EXPECTED_CATEGORIES:
            assert cat in self.html, f"Missing category: {cat!r}"

    def test_five_categories_in_data(self):
        assert len(adp._CATEGORIES) == 5

    def test_category_titles_match_expected(self):
        titles = [c["title"] for c in adp._CATEGORIES]
        assert titles == _EXPECTED_CATEGORIES

    def test_no_script_tag(self):
        assert "<script" not in self.html

    def test_has_style_block(self):
        assert "<style>" in self.html

    def test_print_fits_paper_and_prints_color(self):
        assert "@page" in self.html
        assert "size: Letter;" in self.html
        assert "print-color-adjust: exact;" in self.html

    def test_no_anchor_section(self):
        # Michael Miller's second review: drop "The Anchor" section entirely.
        assert "The Anchor" not in self.html

    def test_ai_resistant_sections_present(self):
        assert "AI-Resistant Moves" in self.html

    def test_ai_forward_sections_present(self):
        assert "AI-Forward Moves" in self.html

    def test_each_category_has_all_facets(self):
        for cat in adp._CATEGORIES:
            assert cat["vulnerable"]
            assert cat["resistant"]
            assert cat["forward"]
            assert cat["tech"]
            assert cat["lanes"]["lane1"]
            assert cat["lanes"]["lane2"]
            assert cat["lanes"]["tilt"]
            assert "anchor" not in cat
            assert "twolane_tilt" not in cat

    def test_moves_are_capped_at_four_bullets(self):
        # Plan cap: each moves-table cell holds at most 4 short bullets.
        for cat in adp._CATEGORIES:
            assert len(cat["resistant"]) <= 4, cat["title"]
            assert len(cat["forward"]) <= 4, cat["title"]

    def test_ai_resistant_renders_before_ai_forward(self):
        # Michael Miller's second review: lead with AI-resistant in each category.
        assert self.html.index("AI-Resistant Moves") < self.html.index("AI-Forward Moves")

    def test_first_resistant_move_no_start_here_prefix(self):
        # "Start here:" prefix removed -- the first bullet's position conveys priority.
        for cat in adp._CATEGORIES:
            first = cat["resistant"][0]
            assert first and not first.startswith("Start here:"), (
                f"{cat['title']}: first AI-Resistant move should not carry a 'Start here:' prefix"
            )

    def test_neutral_non_adversarial_tone(self):
        # Goal is to certify learning, not to catch students or watch them fail.
        assert "catch students or watch them fail" in self.html
        assert "deterrent" not in self.html

    def test_tilt_annotated_models_pairing_present(self):
        assert "TILT" in self.html
        assert "annotated examples" in self.html

    def test_proctoring_and_oral_framed_as_last_resort(self):
        assert "strongest single assurance anchor" not in self.html.lower()
        assert "last-resort" in self.html or "last resort" in self.html

    def test_tech_heading_per_category(self):
        # Restored per-category tech section (Michael's second review): one
        # "Technologies That Help" heading per category, not one consolidated section.
        assert self.html.count("Technologies That Help") == len(adp._CATEGORIES)

    def test_no_standalone_technologies_section(self):
        assert 'id="technologies"' not in self.html

    def test_tech_cell_count_matches_data(self):
        expected = sum(len(cat["tech"]) for cat in adp._CATEGORIES)
        assert self.html.count('<div class="tech-cell">') == expected

    def test_two_lane_tilt_heading_per_category(self):
        assert self.html.count("Two-Lane &amp; TILT Considerations") == len(adp._CATEGORIES)

    def test_two_lane_present(self):
        assert self.html.count("Two-Lane") == len(adp._CATEGORIES)

    def test_moves_table_present(self):
        assert self.html.count('<table class="moves-table">') == len(adp._CATEGORIES)
        assert "<th>AI-Resistant Moves</th>" in self.html
        assert "<th>AI-Forward Moves</th>" in self.html

    def test_considerations_table_present_with_lane_headers(self):
        assert self.html.count('<table class="considerations-table">') == len(adp._CATEGORIES)
        assert "Lane 1" in self.html
        assert "Lane 2" in self.html
        assert self.html.count("<th>TILT</th>") == len(adp._CATEGORIES)

    def test_no_twolane_tilt_string_key_or_anchor_callout(self):
        assert "twolane_tilt" not in self.html
        assert "anchor-callout" not in self.html

    def test_resistant_renders_left_of_forward_in_moves_table(self):
        for cat in adp._CATEGORIES:
            block_start = self.html.index(f'>{cat["title"]}</h3>')
            table_slice = self.html[block_start : block_start + 4000]
            resistant_idx = table_slice.index("AI-Resistant Moves")
            forward_idx = table_slice.index("AI-Forward Moves")
            assert resistant_idx < forward_idx, cat["title"]

    def test_no_dropped_tools(self):
        dropped = [
            "Examity",
            "Perusall",
            "Hypothesis",
            "Turnitin",
            "Canvas Studio",
            "Flip",
            "Mobius",
            "WeBWorK",
            "GitHub",
            "Zoom",
            "Padlet",
        ]
        for tool in dropped:
            assert tool not in self.html, f"dropped tool present: {tool!r}"

    def test_quizzes_ai_forward_merge(self):
        # Michael's second review: Open-AI exams + Meta-items were indistinguishable; merged.
        assert "Open-AI verification" in self.html
        assert "Open-AI exams" not in self.html
        assert "Meta-items" not in self.html

    def test_quizzes_plain_language_auto_vary(self):
        assert "Auto-vary the details" in self.html
        assert "Parameterize where possible" not in self.html

    def test_papers_citation_merge(self):
        assert "Source generation and verification" in self.html
        assert "Citation hallucination hunt" not in self.html
        assert "Teach source verification" not in self.html

    def test_no_randomly_selected_students_phrasing(self):
        assert "randomly selected students" not in self.html

    def test_projects_individual_or_team_reframe(self):
        assert "each member" not in self.html
        assert "individual students or teams" in self.html

    def test_discussions_tone_edit(self):
        assert "prompts that result in generic responses" in self.html
        assert "will not meet the assignment criteria" in self.html
        assert "fails on its face" not in self.html

    def test_papers_tone_edit(self):
        assert (
            "presents the easiest avenue for students to substitute critical thinking "
            "with AI-generated content" in self.html
        )

    def test_presentations_first_resistant_grounds_unique_context(self):
        cat = next(c for c in adp._CATEGORIES if c["title"].startswith("Presentations"))
        assert cat["resistant"][0].startswith("Ground the work in a unique context")
        assert cat["resistant"][-1].startswith("As a last resort for the highest-stakes work")

    def test_no_em_or_en_dash(self):
        assert "—" not in self.html
        assert "–" not in self.html

    def test_front_matter_key_terms_present(self):
        assert "Key Terms" in self.html
        assert "Lane 1: Secured" in self.html
        assert "Secured anchor" in self.html

    def test_front_matter_triage_present(self):
        assert "How to Triage Your Course" in self.html
        assert "Match the Securing Move to Your Enrollment" in self.html
        assert "Grading cost" in self.html
        assert "Accessibility." in self.html
        assert "If you suspect a violation." in self.html

    def test_front_matter_before_categories(self):
        assert self.html.index("Key Terms") < self.html.index('id="categories"')
        assert self.html.index("How to Triage Your Course") < self.html.index('id="categories"')

    def test_each_category_has_sample_artifact_html(self):
        for cat in adp._CATEGORIES:
            artifact = cat["sample_artifact_html"]
            assert "AI-Forward Model" in artifact, cat["title"]
            assert "AI-Resistant Model" in artifact, cat["title"]
            assert "Why it earns credit" in artifact, cat["title"]

    def test_sample_artifacts_render_raw_in_html(self):
        for cat in adp._CATEGORIES:
            assert f'{cat["title"]}: Model Artifacts' in self.html or "Model Artifacts" in self.html
        assert self.html.count("Model Artifacts") == len(adp._CATEGORIES)

    def test_footer_owner_line(self):
        assert "Owner: Michael Miller" in self.html
        assert "Last reviewed: 2026-07-16" in self.html


class TestPlaybookCLI:
    def test_main_writes_output(self, tmp_path):
        out = tmp_path / "playbook.html"
        adp.main(["--output", str(out)])
        assert out.exists()
        content = out.read_text(encoding="utf-8")
        assert "Assessment Design Playbook" in content
        assert "Assessment Accelerator" not in content

    def test_generated_date_in_output(self, tmp_path):
        out = tmp_path / "playbook.html"
        adp.main(["--output", str(out), "--generated-date", "2026-06-29"])
        content = out.read_text(encoding="utf-8")
        assert "2026-06-29" in content
