"""Lightweight color primitives."""

from __future__ import annotations

from dataclasses import dataclass

from . import oklab


@dataclass(slots=True, frozen=True)
class Color:
    """RGB color stored as floats in the [0, 1] range."""

    r: float
    g: float
    b: float
    a: float = 1.0

    def clamp(self) -> Color:
        return Color(
            max(0.0, min(1.0, self.r)),
            max(0.0, min(1.0, self.g)),
            max(0.0, min(1.0, self.b)),
            max(0.0, min(1.0, self.a)),
        )

    def to_hex(self, *, include_alpha: bool = False) -> str:
        r = round(self._clamped(self.r) * 255)
        g = round(self._clamped(self.g) * 255)
        b = round(self._clamped(self.b) * 255)
        if include_alpha:
            a = round(self._clamped(self.a) * 255)
            return f"#{r:02X}{g:02X}{b:02X}{a:02X}"
        return f"#{r:02X}{g:02X}{b:02X}"

    def to_ooxml_rgb(self) -> str:
        """Return color as OOXML sRGB format (uppercase hex, no # prefix).

        OOXML uses RGB values in the format "RRGGBB" for srgbClr elements.
        Example: "#4A90D9" -> "4A90D9"
        """
        return self.to_hex()[1:].upper()

    def to_ooxml_argb(self) -> str:
        """Return color as OOXML ARGB format (uppercase hex, no # prefix).

        Excel uses ARGB values in the format "AARRGGBB" for color elements.
        Example: "#4A90D9" with alpha=1.0 -> "FF4A90D9"
        """
        a = round(self._clamped(self.a) * 255)
        r = round(self._clamped(self.r) * 255)
        g = round(self._clamped(self.g) * 255)
        b = round(self._clamped(self.b) * 255)
        return f"{a:02X}{r:02X}{g:02X}{b:02X}"

    def with_alpha(self, alpha: float) -> Color:
        return Color(self.r, self.g, self.b, alpha)

    def lighten(self, factor: float) -> Color:
        """Lighten by nudging the OKLab lightness."""

        l, a, b = self.to_oklab()
        l = max(0.0, min(1.0, l + factor))
        return Color.from_oklab(l, a, b, self.a)

    def saturate(self, factor: float) -> Color:
        """Increase chroma in OKLCH space."""

        l, c, h = self.to_oklch()
        c = max(0.0, c + factor)
        return Color.from_oklch(l, c, h, self.a)

    def to_oklab(self) -> tuple[float, float, float]:
        return oklab.rgb_to_oklab(
            self._clamped(self.r), self._clamped(self.g), self._clamped(self.b)
        )

    def to_oklch(self) -> tuple[float, float, float]:
        return oklab.rgb_to_oklch(
            self._clamped(self.r), self._clamped(self.g), self._clamped(self.b)
        )

    @classmethod
    def from_oklab(cls, l: float, a: float, b: float, alpha: float = 1.0) -> Color:
        r, g, b = oklab.oklab_to_rgb(l, a, b)
        return cls(r, g, b, alpha).clamp()

    @classmethod
    def from_oklch(cls, l: float, c: float, h: float, alpha: float = 1.0) -> Color:
        r, g, b = oklab.oklch_to_rgb(l, c, h)
        return cls(r, g, b, alpha).clamp()

    @staticmethod
    def _clamped(value: float) -> float:
        return max(0.0, min(1.0, value))


TRANSPARENT = Color(0.0, 0.0, 0.0, 0.0)

__all__ = ["Color", "TRANSPARENT"]
