"""
Tests for the check-css-class-prefix pre-commit hook.

Specifically guards against the regex regression fixed in PR #3103, where
`(?<![\\w/])\\.` matched only the first class in a compound selector like
`.class1.class2` because the leading dot of the second class was preceded
by a word character. That hid 159+ unprefixed compound state classes such
as `.ldr-foo.selected` or `.ldr-foo.loading` from the prefix check.
"""

import subprocess
import sys
import tempfile
from pathlib import Path

import pytest


HOOK_SCRIPT = (
    Path(__file__).parent.parent.parent
    / ".pre-commit-hooks"
    / "check-css-class-prefix.py"
)


def _run_hook(content: str, filename: str) -> subprocess.CompletedProcess:
    """Write content to a temp file and run the hook against it."""
    with tempfile.TemporaryDirectory() as d:
        path = Path(d) / filename
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(content)
        return subprocess.run(
            [sys.executable, str(HOOK_SCRIPT), str(path)],
            capture_output=True,
            text=True,
        )


class TestCompoundSelectorsCSS:
    """The fix in PR #3103: every class in a compound selector must be checked."""

    def test_compound_selector_first_class_unprefixed_is_caught(self):
        """`.bare.ldr-other` must fail on `.bare`."""
        result = _run_hook(".bare.ldr-other { color: red; }\n", "x.css")
        assert result.returncode == 1
        assert "'.bare'" in result.stdout

    def test_compound_selector_second_class_unprefixed_is_caught(self):
        """`.ldr-foo.selected` was previously silently passing — must fail now."""
        result = _run_hook(".ldr-foo.selected { color: red; }\n", "x.css")
        assert result.returncode == 1
        assert "'.selected'" in result.stdout

    def test_compound_selector_three_classes_all_validated(self):
        """`.ldr-a.bare.ldr-c` must fail on the middle bare class."""
        result = _run_hook(".ldr-a.bare.ldr-c { color: red; }\n", "x.css")
        assert result.returncode == 1
        assert "'.bare'" in result.stdout

    def test_compound_selector_all_prefixed_passes(self):
        """`.ldr-foo.ldr-bar` is valid — all classes have the prefix."""
        result = _run_hook(".ldr-foo.ldr-bar { color: red; }\n", "x.css")
        assert result.returncode == 0

    def test_compound_with_pseudo_class_validates_real_classes(self):
        """`.ldr-foo.bare:hover` must fail on `.bare`, not on `:hover`."""
        result = _run_hook(".ldr-foo.bare:hover { color: red; }\n", "x.css")
        assert result.returncode == 1
        assert "'.bare'" in result.stdout
        assert "'.hover'" not in result.stdout


class TestDescendantSelectorsCSS:
    """Space-separated selectors must continue to validate each class."""

    def test_descendant_selector_with_unprefixed_child_fails(self):
        result = _run_hook(".ldr-parent .bare { color: red; }\n", "x.css")
        assert result.returncode == 1
        assert "'.bare'" in result.stdout

    def test_descendant_selector_all_prefixed_passes(self):
        result = _run_hook(".ldr-parent .ldr-child { color: red; }\n", "x.css")
        assert result.returncode == 0


class TestUrlsAreNotMatched:
    """The hook's lookbehind must continue to skip dots inside URL hostnames."""

    def test_url_in_background_image_is_not_flagged(self):
        css = (
            ".ldr-foo {\n"
            "    background: url(https://www.example.com/spinner.svg);\n"
            "}\n"
        )
        result = _run_hook(css, "x.css")
        assert result.returncode == 0

    def test_unquoted_url_with_dotted_path_is_not_flagged(self):
        css = ".ldr-foo { background: url(www.w3.org/path); }\n"
        result = _run_hook(css, "x.css")
        assert result.returncode == 0


class TestAllowlistedClasses:
    """Bootstrap and other framework classes must pass the prefix check."""

    @pytest.mark.parametrize(
        "selector",
        [
            ".container",
            ".form-group",
            ".btn-primary",
            ".active",
            ".disabled",
            ".visually-hidden",
            ".bi-search",
            ".fa-spinner",
        ],
    )
    def test_allowed_framework_class_passes(self, selector: str):
        result = _run_hook(f"{selector} {{ color: red; }}\n", "x.css")
        assert result.returncode == 0

    def test_compound_with_allowlisted_modifier_passes(self):
        """`.ldr-foo.active` must pass — `active` is allowlisted."""
        result = _run_hook(".ldr-foo.active { color: red; }\n", "x.css")
        assert result.returncode == 0


class TestCommentsAndImportsSkipped:
    """Lines that are CSS comments or @import should not be scanned."""

    def test_unprefixed_class_inside_comment_is_ignored(self):
        css = "/* .bare-class is mentioned but not defined here */\n"
        result = _run_hook(css, "x.css")
        assert result.returncode == 0

    def test_import_statement_is_skipped(self):
        css = '@import url("./other.bare-thing.css");\n'
        result = _run_hook(css, "x.css")
        assert result.returncode == 0


class TestStyleTagInHTML:
    """The HTML scanner uses the same regex inside <style> blocks."""

    def test_compound_selector_in_style_tag_is_caught(self):
        html = (
            "<html><head><style>\n"
            ".ldr-foo.bare { color: red; }\n"
            "</style></head></html>\n"
        )
        result = _run_hook(html, "x.html")
        assert result.returncode == 1
        assert "'.bare'" in result.stdout
        assert "in style tag" in result.stdout

    def test_compound_selector_outside_style_tag_falls_through_to_attr_check(
        self,
    ):
        """A `.ldr-foo.bare` literal outside <style> isn't a CSS rule definition;
        the attribute scanner only inspects class="..." attributes, so it
        shouldn't flag this content as either a CSS or HTML class issue."""
        html = '<div data-css=".ldr-foo.bare">x</div>\n'
        result = _run_hook(html, "x.html")
        assert result.returncode == 0
