"""Tests for WCAG 2.1 A+AA wiring into preflight_checks.py (PR2).

Guards docs/plan-wcag-pr2-preflight-wiring.md:
- dict->Issue adapter maps failures->warning, warnings->info, parses SC
- dedupe by (criterion, message)
- all 4 content surfaces emit WCAG findings from their HTML field
- fragment mode suppresses document-level 3.1.1 / 1.4.10
- net-new criteria: 1.4.1 (de-underlined link), 1.4.4 (viewport), 2.4.2 (title)
- severity floor: no WCAG finding is ever 'error'
"""

import os
import sys
import unittest

HERE = os.path.dirname(os.path.abspath(__file__))
SCRIPTS = os.path.join(HERE, "..", "scripts")
sys.path.insert(0, SCRIPTS)

import preflight_checks as pf  # noqa: E402


def _wcag(issues):
    return [i for i in issues if i.criterion_id.startswith("a11y.wcag:")]


def _ids(issues):
    return sorted(i.criterion_id for i in _wcag(issues))


class AdapterTest(unittest.TestCase):
    def test_failure_maps_to_warning(self):
        # low-contrast inline -> 1.4.3 failure -> warning
        issues = pf._wcag_issues('<p style="color:#777;background-color:#888">x</p>')
        hit = [i for i in issues if i.criterion_id == "a11y.wcag:1.4.3"]
        self.assertTrue(hit)
        self.assertEqual(hit[0].severity, "warning")

    def test_severity_floor_never_error(self):
        html = ('<img src="x.png"><h3>skip</h3>'
                '<p style="color:#777;background-color:#888">x</p>'
                '<input type="text">')
        for i in pf._wcag_issues(html):
            self.assertNotEqual(i.severity, "error")

    def test_fragment_suppresses_document_level(self):
        # body fragment has no <html lang> / viewport; must NOT fire 3.1.1/1.4.10
        ids = _ids(pf._wcag_issues("<p>hello</p>", fragment=True))
        self.assertNotIn("a11y.wcag:3.1.1", ids)
        self.assertNotIn("a11y.wcag:1.4.10", ids)

    def test_dedupe_identical_snippets(self):
        snippet = '<p style="color:#777;background-color:#888">x</p>'
        issues = pf._wcag_issues(snippet + snippet)
        hits = [i for i in issues if i.criterion_id == "a11y.wcag:1.4.3"]
        self.assertEqual(len(hits), 1)


class NetNewCriteriaTest(unittest.TestCase):
    def test_141_de_underlined_link(self):
        ids = _ids(pf._wcag_issues('<a href="#" style="color:#06c;text-decoration:none">go</a>'))
        self.assertIn("a11y.wcag:1.4.1", ids)

    def test_141_underlined_link_clean(self):
        ids = _ids(pf._wcag_issues('<a href="#" style="color:#06c">go</a>'))
        self.assertNotIn("a11y.wcag:1.4.1", ids)

    def test_144_viewport_user_scalable_no(self):
        html = '<meta name="viewport" content="width=device-width, user-scalable=no">'
        ids = _ids(pf._wcag_issues(html))
        self.assertIn("a11y.wcag:1.4.4", ids)

    def test_144_viewport_max_scale_low(self):
        html = '<meta name="viewport" content="width=device-width, maximum-scale=1.0">'
        ids = _ids(pf._wcag_issues(html))
        self.assertIn("a11y.wcag:1.4.4", ids)

    def test_242_empty_title(self):
        self.assertTrue(pf._wcag_title_issue("", "page"))
        self.assertTrue(pf._wcag_title_issue("Untitled", "page"))

    def test_242_present_title(self):
        self.assertEqual(pf._wcag_title_issue("Module 1 Overview", "page"), [])


class ContentSurfaceTest(unittest.TestCase):
    LOW_CONTRAST = '<p style="color:#777;background-color:#888">hi</p>'

    def test_check_page_emits_wcag(self):
        issues = pf.check_page(self.LOW_CONTRAST, context={"title": "M1"})
        self.assertIn("a11y.wcag:1.4.3", _ids(issues))

    def test_check_page_missing_control_name(self):
        issues = pf.check_page('<input type="text">', context={"title": "M1"})
        self.assertIn("a11y.wcag:4.1.2", _ids(issues))

    def test_check_assessment_emits_wcag_from_description(self):
        issues = pf.check_assessment({"name": "HW1", "description": self.LOW_CONTRAST})
        self.assertIn("a11y.wcag:1.4.3", _ids(issues))

    def test_check_discussion_emits_wcag_from_message(self):
        issues = pf.check_discussion({"title": "D1", "message": self.LOW_CONTRAST})
        self.assertIn("a11y.wcag:1.4.3", _ids(issues))

    def test_check_quiz_emits_wcag_from_question(self):
        quiz = {"title": "Q1", "questions": [{"question_text": self.LOW_CONTRAST}]}
        issues = pf.check_quiz(quiz)
        self.assertIn("a11y.wcag:1.4.3", _ids(issues))

    def test_check_quiz_emits_242_for_untitled(self):
        issues = pf.check_quiz({"questions": []})
        self.assertIn("a11y.wcag:2.4.2", _ids(issues))

    def test_assessment_title_not_double_counted(self):
        # delegated body scan must NOT add a page-level 2.4.2 on top of the
        # assignment's own title check
        issues = pf.check_assessment({"name": "HW1", "description": "<p>ok</p>"})
        self.assertEqual([i for i in _wcag(issues) if i.criterion_id == "a11y.wcag:2.4.2"], [])


class Tier1RoutingTest(unittest.TestCase):
    def test_page_tier1_surfaces_wcag(self):
        tier1 = pf.check_page_tier1('<p style="color:#777;background-color:#888">x</p>', slug="m1-overview")
        self.assertIn("a11y.wcag:1.4.3", sorted(i.criterion_id for i in tier1))

    def test_quiz_tier1_excludes_wcag(self):
        quiz = {"title": "Q1", "questions": [{"question_text": '<input type="text">'}]}
        tier1 = pf.check_quiz_tier1(quiz)
        self.assertEqual([i for i in tier1 if i.criterion_id.startswith("a11y.wcag:")], [])


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