"""The dynamic-property vocabulary that drives the generated QSS.

Widgets are styled by ``mgf*`` dynamic properties (``mgfVariant``,
``mgfClass``, ``mgfChrome``, ``mgfState``) that the stylesheet selects
on. Properties can be declared statically in a ``.ui`` file
(``stdset="0"`` — the preferred, declarative route used by the C2
sweep) or set at runtime via :func:`set_mgf`, which re-polishes the
widget so Qt re-evaluates the selectors.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from PySide6.QtGui import QFont

from mgf.brand_qt.fonts import mono_font
from mgf.brand_qt.qss import MGF_PROPS
from mgf.brand_qt.tokens import TEXT_PT, WEIGHT

if TYPE_CHECKING:
    from PySide6.QtWidgets import QLabel, QWidget


def set_mgf(widget: QWidget, **props: str) -> None:
    """Set ``mgf<Key>`` dynamic properties and re-polish the widget.

    ``set_mgf(button, Variant="accent")`` sets ``mgfVariant``.
    Re-polishing is required for property selectors to re-apply on a
    widget that is already styled.

    Keys and values are validated against
    :data:`mgf.brand_qt.qss.MGF_PROPS` — the vocabulary the generated
    stylesheet actually selects on. A key or value outside it would
    style nothing, silently; that no-op once shipped an entire
    application shell unstyled through four green gate runs
    (PAPER-10), so it raises here instead.
    """
    for key, value in props.items():
        allowed = MGF_PROPS.get(key)
        if allowed is None:
            hint = ""
            if key.startswith("mgf"):
                bare = key[3:]
                hint = (
                    f" — pass the bare key ({bare!r}), not the prefixed"
                    f" property name; set_mgf adds the 'mgf' prefix itself"
                )
            raise ValueError(
                f"unknown mgf property key {key!r}{hint};"
                f" known keys: {', '.join(sorted(MGF_PROPS))}"
            )
        if value not in allowed:
            raise ValueError(
                f"unknown value {value!r} for mgf property {key!r};"
                f" the stylesheet selects on: {', '.join(sorted(allowed))}"
            )
        widget.setProperty(f"mgf{key[0].upper()}{key[1:]}", value)
    style = widget.style()
    style.unpolish(widget)
    style.polish(widget)


def apply_label_class(label: QLabel, klass: str) -> None:
    """Tag a label with ``mgfClass`` plus the font details QSS cannot do.

    Most classes are fully QSS-driven; ``eyebrow`` additionally needs
    letter-spacing (no QSS property exists), applied here. Upper-casing
    the text is the caller's choice.
    """
    if klass == "eyebrow":
        font = mono_font(TEXT_PT["xs"], WEIGHT["medium"])
        font.setLetterSpacing(QFont.SpacingType.PercentageSpacing, 112)
        label.setFont(font)
    set_mgf(label, Class=klass)


__all__ = ["apply_label_class", "set_mgf"]
