"""Status chips and badges — colour + dot + label, never colour alone.

The Qt translation of the web kit's ``.status-chip``/``.badge``
(00-brand §5). Chip surfaces (tint background, strong text, soft
border) are QSS-driven via the ``mgfChip``/``mgfState`` properties and
therefore re-theme automatically; the dot paints the status *base*
hue, which is theme-invariant by brand design — so chips need no
manual re-theme wiring at all.

States: ``success`` (teal) · ``warning`` · ``error`` · ``info`` ·
``busy`` (pulsing dot) · ``derived`` (violet) · ``neutral`` (slate) ·
``quarantine`` (violet family; the name cell carries the lock icon).
"""

from __future__ import annotations

from PySide6.QtCore import QEasingCurve, QVariantAnimation
from PySide6.QtGui import QColor, QPainter
from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QWidget

from mgf.brand_qt.fonts import mono_font
from mgf.brand_qt.motion import reduce_motion
from mgf.brand_qt.tokens import STATE_HUES, TEXT_PT, WEIGHT
from mgf.brand_qt.widgets.props import set_mgf

_DOT_PX = 10
_PULSE_MS = 1200


class _Dot(QWidget):
    """A filled circle in the state hue; opacity is animatable."""

    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setFixedSize(_DOT_PX, _DOT_PX)
        self._color = QColor(STATE_HUES["neutral"])
        self._opacity = 1.0

    def set_hue(self, hex_color: str) -> None:
        self._color = QColor(hex_color)
        self.update()

    def set_opacity(self, value: float) -> None:
        self._opacity = value
        self.update()

    def paintEvent(self, event: object) -> None:
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        painter.setOpacity(self._opacity)
        painter.setBrush(self._color)
        painter.setPen(QColor(0, 0, 0, 0))
        painter.drawEllipse(self.rect())
        painter.end()


class StatusChip(QFrame):
    """Dot + mono label pill. ``busy`` pulses unless animation is off."""

    def __init__(
        self,
        state: str = "neutral",
        label: str | None = None,
        *,
        animate: bool = True,
        parent: QWidget | None = None,
    ) -> None:
        super().__init__(parent)
        self._animate = animate
        self._pulse: QVariantAnimation | None = None
        layout = QHBoxLayout(self)
        layout.setContentsMargins(10, 3, 10, 3)
        layout.setSpacing(6)
        self._dot = _Dot(self)
        layout.addWidget(self._dot)
        self._label = QLabel(self)
        self._label.setFont(mono_font(TEXT_PT["sm"], WEIGHT["medium"]))
        layout.addWidget(self._label)
        set_mgf(self, Chip="true")
        self.set_state(state, label)

    def set_state(self, state: str, label: str | None = None) -> None:
        """Switch the chip's state family (and optionally its text)."""
        if state not in STATE_HUES:
            raise ValueError(f"unknown chip state {state!r}")
        self._state = state
        self._dot.set_hue(STATE_HUES[state])
        if label is not None:
            self._label.setText(label)
        set_mgf(self, State=state)
        self._sync_pulse()

    def set_text(self, text: str) -> None:
        self._label.setText(text)

    def text(self) -> str:
        return self._label.text()

    @property
    def state(self) -> str:
        return self._state

    def set_animate(self, animate: bool) -> None:
        """Turn the busy pulse on or off for this chip (PAPER-02).

        ``_animate`` used to be constructor-frozen, so a consumer
        honouring a live reduce-motion toggle had to rebuild the chip.
        """
        self._animate = animate
        self._sync_pulse()

    def showEvent(self, event: object) -> None:
        super().showEvent(event)  # type: ignore[arg-type]
        self._sync_pulse()

    def hideEvent(self, event: object) -> None:
        # A hidden chip must not keep an infinite QVariantAnimation
        # ticking the event loop (PAPER-02): stop on hide, resume on
        # show. Docks and stacked pages hide chips for hours.
        super().hideEvent(event)  # type: ignore[arg-type]
        self._sync_pulse()

    def _sync_pulse(self) -> None:
        wants_pulse = (
            self._animate and self._state == "busy" and self.isVisible() and not reduce_motion()
        )
        if wants_pulse and self._pulse is None:
            pulse = QVariantAnimation(self)
            pulse.setStartValue(0.35)
            pulse.setEndValue(1.0)
            pulse.setDuration(_PULSE_MS)
            pulse.setEasingCurve(QEasingCurve.Type.InOutSine)
            pulse.setLoopCount(-1)
            pulse.valueChanged.connect(
                lambda v: self._dot.set_opacity(float(v)),
            )
            pulse.start()
            self._pulse = pulse
        elif not wants_pulse and self._pulse is not None:
            self._pulse.stop()
            self._pulse.deleteLater()
            self._pulse = None
            self._dot.set_opacity(1.0)


class Badge(StatusChip):
    """A StatusChip that never animates (static status marker)."""

    def __init__(
        self,
        state: str = "neutral",
        label: str | None = None,
        parent: QWidget | None = None,
    ) -> None:
        super().__init__(state, label, animate=False, parent=parent)


__all__ = ["STATE_HUES", "Badge", "StatusChip"]
