"""WCAG 2.2 contrast guard over every text-on-background token pair.

The brand book verified these pairs upstream; this test keeps them
verified here forever (token drift or a bad re-vendor fails loudly).
Body-size text needs ≥ 4.5:1; large text and UI glyphs need ≥ 3:1.
Qt-free.
"""

from __future__ import annotations

import pytest

from mgf.brand_qt import tokens
from mgf.brand_qt._mix import _channels


def _luminance(hex_color: str) -> float:
    def channel(value: int) -> float:
        srgb = value / 255.0
        return srgb / 12.92 if srgb <= 0.04045 else ((srgb + 0.055) / 1.055) ** 2.4

    r, g, b = _channels(hex_color)
    return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b)


def contrast(a: str, b: str) -> float:
    la, lb = _luminance(a), _luminance(b)
    lighter, darker = max(la, lb), min(la, lb)
    return (lighter + 0.05) / (darker + 0.05)


def _body_pairs(sem: tokens.SemanticColors) -> list[tuple[str, str, str]]:
    """Pairs carrying body-size prose — the 4.5:1 tier."""
    return [
        ("text-primary/canvas", sem.text_primary, sem.bg_canvas),
        ("text-primary/surface", sem.text_primary, sem.bg_surface),
        ("text-secondary/canvas", sem.text_secondary, sem.bg_canvas),
        ("link/canvas", sem.link, sem.bg_canvas),
        ("action-primary text/bg", sem.action_primary_text, sem.action_primary_bg),
        ("chrome mist/ink", tokens.RAW.mist, tokens.RAW.ink),
    ]


def _chip_pairs(sem: tokens.SemanticColors) -> list[tuple[str, str, str]]:
    """Chip-label strong-on-tint pairs.

    As of 0.1.2 these hold the full 4.5:1 text tier (PAPER-07): the
    four recipes that measured 3.63-4.40:1 (light warning, dark
    info/busy/derived) were strengthened kit-side — tint surfaces kept
    where possible, the locked RAW palette untouched.
    """
    pairs = []
    for family in ("error", "warning", "info", "success"):
        pairs.append(
            (
                f"status-{family} strong/tint",
                getattr(sem, f"status_{family}_strong"),
                getattr(sem, f"status_{family}_tint"),
            )
        )
    for family in ("busy", "derived"):
        pairs.append(
            (
                f"state-{family} strong/tint",
                getattr(sem, f"state_{family}_strong"),
                getattr(sem, f"state_{family}_tint"),
            )
        )
    return pairs


@pytest.mark.parametrize("theme_name", ["light", "dark"])
def test_body_text_pairs_meet_aa(theme_name: str) -> None:
    sem = tokens.LIGHT if theme_name == "light" else tokens.DARK
    failures = []
    for label, fg, bg in _body_pairs(sem):
        if not (fg.startswith("#") and bg.startswith("#")):
            continue  # rgba grounds composite over surfaces; skip raw-alpha pairs
        ratio = contrast(fg, bg)
        if ratio < 4.5:
            failures.append(f"{theme_name} {label}: {ratio:.2f}")
    assert not failures, failures


@pytest.mark.parametrize("theme_name", ["light", "dark"])
def test_chip_pairs_meet_the_ui_component_tier(theme_name: str) -> None:
    sem = tokens.LIGHT if theme_name == "light" else tokens.DARK
    failures = []
    for label, fg, bg in _chip_pairs(sem):
        if not (fg.startswith("#") and bg.startswith("#")):
            continue
        ratio = contrast(fg, bg)
        if ratio < 3.0:
            failures.append(f"{theme_name} {label}: {ratio:.2f}")
    assert not failures, failures


@pytest.mark.parametrize("theme_name", ["light", "dark"])
def test_chip_strong_on_tint_meets_aa_text(theme_name: str) -> None:
    """PAPER-07: every chip label reads at the 4.5:1 TEXT tier.

    This is the gate the fix ships with — the shrunk 3:1 reading above
    stays as the floor, this pins the AA promise consumers (hrb-app G10)
    were blocked on.
    """
    sem = tokens.LIGHT if theme_name == "light" else tokens.DARK
    failures = []
    for label, fg, bg in _chip_pairs(sem):
        if not (fg.startswith("#") and bg.startswith("#")):
            continue
        ratio = contrast(fg, bg)
        if ratio < 4.5:
            failures.append(f"{theme_name} {label}: {ratio:.2f}")
    assert not failures, failures


def test_brand_laws_hold_numerically() -> None:
    # The two upstream contrast facts that shaped the focus-ring law.
    assert contrast(tokens.RAW.teal, tokens.RAW.paper) < 3.0  # why light link is darkened
    assert contrast(tokens.RAW.gold, tokens.RAW.paper) < 3.0  # why gold is never light focus
    assert contrast(tokens.RAW.ink, tokens.RAW.gold) >= 4.5  # gold CTAs carry Ink text
    assert contrast(tokens.RAW.mist, tokens.RAW.indigo) >= 7.0  # chrome AAA
