"""Tests for preflight_checks.check_staging_meta_completeness."""

import json
import pathlib
import sys


SCRIPTS_DIR = pathlib.Path(__file__).resolve().parents[1] / "scripts"
sys.path.insert(0, str(SCRIPTS_DIR))

import preflight_checks  # noqa: E402


def _ids(issues):
    return {i.criterion_id for i in issues}


def test_warns_on_missing_title_and_module_with_no_fallback():
    issues = preflight_checks.check_staging_meta_completeness(
        meta=None,
        obj_id="random-slug",
        object_type="assignments",
        content="<p>no headings here</p>",
    )
    assert "staging.meta.missing_title" in _ids(issues)
    assert "staging.meta.missing_module" in _ids(issues)
    assert all(i.severity == "warning" for i in issues)


def test_no_warning_when_meta_title_and_module_present():
    issues = preflight_checks.check_staging_meta_completeness(
        meta={"title": "HW Review", "module_number": 2},
        obj_id="random-slug",
        object_type="assignments",
        content="<p>x</p>",
    )
    assert issues == []


def test_no_title_warning_when_heading_fallback_resolves():
    issues = preflight_checks.check_staging_meta_completeness(
        meta=None,
        obj_id="m2-hw",
        object_type="assignments",
        content="<h3>Module 2 Homework</h3>",
    )
    ids = _ids(issues)
    assert "staging.meta.missing_title" not in ids
    assert "staging.meta.missing_module" not in ids


def test_no_module_warning_when_filename_pattern_matches():
    issues = preflight_checks.check_staging_meta_completeness(
        meta={"title": "HW"},
        obj_id="m3-something",
        object_type="assignments",
        content="<p>x</p>",
    )
    assert "staging.meta.missing_module" not in _ids(issues)


def test_quiz_json_title_fallback_resolves_title():
    body = json.dumps({"quiz": {"title": "Quiz Title"}})
    issues = preflight_checks.check_staging_meta_completeness(
        meta={"module_number": 1},
        obj_id="m1-quiz",
        object_type="quizzes",
        content=body,
    )
    assert "staging.meta.missing_title" not in _ids(issues)


def test_module_pattern_module_dash_form():
    issues = preflight_checks.check_staging_meta_completeness(
        meta={"title": "x"},
        obj_id="module-2-overview",
        object_type="pages",
        content="<h2>x</h2>",
    )
    assert "staging.meta.missing_module" not in _ids(issues)


# ── check_table_header_contrast ──────────────────────────────────────────

_CRIT = "table.header.row_bg_without_cell_bg"


def test_row_bg_without_cell_bg_warns():
    html = (
        '<table><tr style="background:#8C1D40;color:#fff">'
        "<th>Criterion</th><th>Points</th></tr>"
        "<tr><td>Clarity</td><td>10</td></tr></table>"
    )
    issues = preflight_checks.check_table_header_contrast(html)
    assert _CRIT in _ids(issues)
    assert all(i.severity == "warning" for i in issues)


def test_row_bg_with_each_cell_bg_no_warn():
    html = (
        '<table><tr style="background:#8C1D40;color:#fff">'
        '<th style="background:#8C1D40;color:#fff">Criterion</th>'
        '<th style="background:#8C1D40;color:#fff">Points</th></tr></table>'
    )
    issues = preflight_checks.check_table_header_contrast(html)
    assert _CRIT not in _ids(issues)


def test_no_row_bg_no_warn():
    html = "<table><tr><th>Criterion</th><th>Points</th></tr></table>"
    issues = preflight_checks.check_table_header_contrast(html)
    assert _CRIT not in _ids(issues)


# ── check_no_em_dash ──────────────────────────────────────────────────────

_DASH_CRIT = "style.em_dash"


def test_literal_em_dash_warns():
    issues = preflight_checks.check_no_em_dash("<p>foo — bar</p>")
    assert _DASH_CRIT in _ids(issues)
    assert all(i.severity == "warning" for i in issues)


def test_mdash_entity_warns():
    issues = preflight_checks.check_no_em_dash("<p>foo &mdash; bar</p>")
    assert _DASH_CRIT in _ids(issues)


def test_ndash_entity_warns():
    issues = preflight_checks.check_no_em_dash("<p>10&ndash;15 min</p>")
    assert _DASH_CRIT in _ids(issues)


def test_literal_en_dash_warns():
    issues = preflight_checks.check_no_em_dash("<p>10 – 15 min</p>")
    assert _DASH_CRIT in _ids(issues)


def test_plain_hyphen_no_warn():
    issues = preflight_checks.check_no_em_dash("<p>foo - bar low-stakes</p>")
    assert _DASH_CRIT not in _ids(issues)


def test_clean_punctuation_no_warn():
    issues = preflight_checks.check_no_em_dash("<p>foo, bar: baz</p>")
    assert issues == []


def test_many_dashes_capped_with_summary():
    # 15 em dashes -> 10 individual issues + 1 summary "and N more" issue.
    html = "<p>" + "a — b " * 15 + "</p>"
    issues = preflight_checks.check_no_em_dash(html)
    assert all(i.criterion_id == _DASH_CRIT for i in issues)
    assert len(issues) == 11  # 10 capped + 1 summary
    summary = issues[-1]
    assert "and 5 more" in summary.message


def test_check_page_wires_em_dash_lint():
    issues = preflight_checks.check_page("<h2>Title</h2><p>foo &mdash; bar</p>")
    assert _DASH_CRIT in _ids(issues)


def test_staged_image_gate_flags_direct_canvas_file_url():
    # A page image embedded as a live Canvas /files/ URL bypasses local-first staging.
    html = '<h2>T</h2><div><img src="/courses/262726/files/133386246/preview" alt="chart"></div>'
    msgs = [i.message for i in preflight_checks.check_page(html)]
    assert any("Page Image Staging Gate" in m for m in msgs)


def test_staged_image_gate_ok_for_token():
    # The asucb-staged-file:// token is the correct local-first path — no flag.
    html = '<h2>T</h2><div><img src="asucb-staged-file://brk-win" alt="chart"></div>'
    msgs = [i.message for i in preflight_checks.check_page(html)]
    assert not any("Page Image Staging Gate" in m for m in msgs)


# ── check_inline_font_weight ──────────────────────────────────────────────

_FW_CRIT = "style.font_weight_inline"


def test_font_weight_in_style_attr_warns():
    html = '<p style="color:#8C1D40;font-weight:bold;">Label</p>'
    issues = preflight_checks.check_inline_font_weight(html)
    assert _FW_CRIT in _ids(issues)
    assert all(i.severity == "warning" for i in issues)


def test_font_weight_in_single_quoted_style_attr_warns():
    html = "<span style='font-weight: 600'>Label</span>"
    issues = preflight_checks.check_inline_font_weight(html)
    assert _FW_CRIT in _ids(issues)


def test_font_weight_outside_style_attr_no_finding():
    # Prose/<strong> mentions of the property name are not style declarations.
    html = ("<p>Canvas strips font-weight from inline styles; "
            "use <strong>strong</strong> instead.</p>")
    issues = preflight_checks.check_inline_font_weight(html)
    assert issues == []


def test_clean_body_no_finding():
    html = '<h2>Title</h2><p style="color:#333;">Body text</p>'
    issues = preflight_checks.check_inline_font_weight(html)
    assert issues == []


def test_multiple_font_weight_attrs_each_flagged():
    html = ('<p style="font-weight:bold;">A</p>'
            '<div style="color:#000;">clean</div>'
            '<span style="font-weight:700;">B</span>')
    issues = preflight_checks.check_inline_font_weight(html)
    assert len(issues) == 2
    assert all(i.criterion_id == _FW_CRIT for i in issues)


def test_check_page_wires_font_weight_lint():
    issues = preflight_checks.check_page(
        '<h2>Title</h2><p style="font-weight:bold;">x</p>')
    assert _FW_CRIT in _ids(issues)


def test_page_tier1_surfaces_font_weight_lint():
    issues = preflight_checks.check_page_tier1(
        '<h2>Title</h2><p style="font-weight:bold;">x</p>', slug="m1-overview")
    assert _FW_CRIT in _ids(issues)


def test_quiz_tier1_flags_font_weight_in_description_and_questions():
    quiz = {
        "title": "Quiz",
        "description": '<p style="font-weight:600;">Intro</p>',
        "questions": [
            {"question_name": "Q1",
             "question_text": '<span style="font-weight:bold;">Pick one</span>'},
        ],
    }
    issues = preflight_checks.check_quiz_tier1(quiz)
    fw = [i for i in issues if i.criterion_id == _FW_CRIT]
    assert len(fw) == 2
