"""Unit tests for the Magogi token layer (Qt-free).

Guards the invariants from ``docs/plans/desktop-ui-redesign/``:
theme parity by construction, value validity, upstream color-mix
parity spot-checks, viz-order lock, and the palette lint (no raw hex
outside the token core).
"""

from __future__ import annotations

import re
import subprocess
import sys
from dataclasses import fields
from pathlib import Path

from mgf.brand_qt import tokens

_HEX_RE = re.compile(r"^#[0-9a-f]{6}$")
_RGBA_RE = re.compile(r"^rgba\(\d{1,3},\d{1,3},\d{1,3},0\.\d{1,2}\)$")

_DESIGN_SRC = Path(tokens.__file__).parent
_HEX_IN_SOURCE_RE = re.compile(r"#[0-9a-fA-F]{6}\b")

# The only modules allowed to carry raw hex literals (the token core).
_HEX_ALLOWED = {"tokens.py", "_mix.py"}


def test_light_and_dark_share_the_exact_field_set() -> None:
    light_fields = {f.name for f in fields(tokens.LIGHT)}
    dark_fields = {f.name for f in fields(tokens.DARK)}
    assert light_fields == dark_fields
    assert len(light_fields) == 39


def test_every_semantic_value_is_hex_or_rgba() -> None:
    for theme in (tokens.LIGHT, tokens.DARK):
        for f in fields(theme):
            value = getattr(theme, f.name)
            assert _HEX_RE.match(value) or _RGBA_RE.match(value), (f.name, value)


def test_expected_mix_results_from_the_design_doc() -> None:
    # 03-target-design.md §4 spot-check table.
    assert tokens.LIGHT.link == "#1c7568"
    assert tokens.LIGHT.link_hover == "#165c52"
    assert tokens.DARK.link == "#4bc2b1"
    assert tokens.DARK.text_secondary == "#a5abb5"
    assert tokens.DARK.border_strong == "#c6c9d0"
    assert tokens.LIGHT.status_error_tint == "#fbe2e3"
    assert tokens.DARK.status_error_tint == "#4c232c"
    assert tokens.LIGHT.state_busy_strong == "#216090"
    assert tokens.DARK.status_success_strong == "#51c4b4"


def test_brand_contrast_laws_are_encoded() -> None:
    # Gold never the light focus ring; gold IS the dark ring (brand F2).
    assert tokens.LIGHT.focus_ring != tokens.RAW.gold
    assert tokens.DARK.focus_ring == tokens.RAW.gold
    # Raw teal is never the light link (fails AA on Paper).
    assert tokens.LIGHT.link != tokens.RAW.teal
    # Gold CTAs carry Ink text, never white.
    assert tokens.LIGHT.action_primary_text == tokens.RAW.ink
    assert tokens.DARK.action_primary_text == tokens.RAW.ink


def test_viz_categorical_order_is_locked() -> None:
    assert len(tokens.VIZ_CATEGORICAL) == 6
    assert tokens.VIZ_CATEGORICAL[0] == tokens.RAW.gold
    assert tokens.VIZ_CATEGORICAL[1] == tokens.RAW.teal
    assert len(tokens.VIZ_SEQ_INDIGO) == 5
    assert len(tokens.VIZ_DIVERGING) == 7
    assert tokens.VIZ_DIVERGING[3] == tokens.RAW.mist


def test_scales_match_the_design_doc() -> None:
    assert tokens.SPACE[4] == 16
    assert tokens.SPACE[24] == 96
    assert set(tokens.RADIUS) == {"sm", "md", "lg", "tile", "pill"}
    assert tokens.TEXT_PT["base"] == 10.5
    assert tokens.WEIGHT["semibold"] == 600
    assert tokens.DURATION_MS["fast"] == 120


def test_palette_lint_no_raw_hex_outside_the_token_core() -> None:
    """No GUI module outside the token core may carry a hex literal.

    Covers the whole kit package.
    """
    gui_root = _DESIGN_SRC.parent
    offenders: list[str] = []
    for path in gui_root.rglob("*.py"):
        if path.name in _HEX_ALLOWED or "__pycache__" in path.parts:
            continue
        if _HEX_IN_SOURCE_RE.search(path.read_text(encoding="utf-8")):
            offenders.append(str(path))
    assert offenders == []


def test_token_core_is_qt_free() -> None:
    """Importing the design package must not pull in PySide6.

    Checked in a fresh interpreter: the surrounding suite legitimately
    loads PySide6, so an in-process ``sys.modules`` check would lie.
    """
    probe = (
        "import sys; import mgf.brand_qt; "
        "leaked = [m for m in sys.modules if m.split('.')[0] == 'PySide6']; "
        "assert not leaked, f'PySide6 leaked into the token core: {leaked}'"
    )
    subprocess.run([sys.executable, "-c", probe], check=True, timeout=60)  # noqa: S603 - fixed argv


def test_mode_and_mix_are_public_from_the_qt_free_core() -> None:
    """PAPER-09 + PAPER-05: settings models import Mode without PySide6."""
    import mgf.brand_qt as kit
    from mgf.brand_qt.tokens import Mode  # noqa: F401 - the Qt-free path

    assert "Mode" in kit.__all__
    assert "mix" in kit.__all__
    assert "MGF_PROPS" in kit.__all__
    assert kit.mix("#000000", 50, "#ffffff") == "#808080"
    assert set(kit.MGF_PROPS) == {"Accent", "Chip", "Chrome", "Class", "State", "Variant"}
