"""Color transform helpers shared across token generation and validation."""

from __future__ import annotations

import colorsys
from typing import Dict, Iterable, Mapping, cast

from .models import Color
from .parser import parse_color


def _parse_hex(value: str) -> Color:
    color = parse_color(value)
    if color is None:
        raise ValueError(f"Invalid hex color: {value}")
    return color


def rgb_to_hex(rgb: Iterable[float]) -> str:
    r, g, b = rgb
    return (
        f"#{int(round(r * 255)):02X}{int(round(g * 255)):02X}{int(round(b * 255)):02X}"
    )


def _read_hsl(hsl: Mapping[str, float] | Iterable[float]) -> tuple[float, float, float]:
    if isinstance(hsl, Mapping):
        m = cast(Mapping[str, float], hsl)
        return float(m["h"]), float(m["s"]), float(m["l"])
    values = list(hsl)
    if len(values) != 3:
        raise ValueError("HSL must contain three values")
    return float(values[0]), float(values[1]), float(values[2])


def hex_to_hsl(hex_color: str) -> Dict[str, float]:
    color = _parse_hex(hex_color)
    h, l, s = colorsys.rgb_to_hls(color.r, color.g, color.b)
    return {"h": (h * 360.0) % 360.0, "s": s * 100.0, "l": l * 100.0}


def hsl_to_hex(hsl: Mapping[str, float] | Iterable[float]) -> str:
    h, s, l = _read_hsl(hsl)
    r, g, b = colorsys.hls_to_rgb((h % 360.0) / 360.0, l / 100.0, s / 100.0)
    return rgb_to_hex((r, g, b))


def lighten(hex_color: str, factor: float) -> str:
    color = _parse_hex(hex_color)
    h, l, s = colorsys.rgb_to_hls(color.r, color.g, color.b)
    l = min(1.0, l + factor)
    r2, g2, b2 = colorsys.hls_to_rgb(h, l, s)
    return rgb_to_hex((r2, g2, b2))


def darken(hex_color: str, factor: float) -> str:
    color = _parse_hex(hex_color)
    h, l, s = colorsys.rgb_to_hls(color.r, color.g, color.b)
    l = max(0.0, l - factor)
    r2, g2, b2 = colorsys.hls_to_rgb(h, l, s)
    return rgb_to_hex((r2, g2, b2))


def hsl_rotate(
    hsl: Mapping[str, float] | Iterable[float], degrees: float
) -> Dict[str, float]:
    h, s, l = _read_hsl(hsl)
    return {"h": (h + degrees) % 360.0, "s": s, "l": l}


def desaturate(
    hsl: Mapping[str, float] | Iterable[float], factor: float
) -> Dict[str, float]:
    h, s, l = _read_hsl(hsl)
    return {"h": h, "s": max(0.0, s * (1.0 - factor)), "l": l}


def contrast_color(hex_color: str, target_ratio: float = 4.5) -> str:
    def luminance(rgb: tuple[float, float, float]) -> float:
        def channel(c: float) -> float:
            return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4

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

    def ratio(fg: tuple[float, float, float], bg: tuple[float, float, float]) -> float:
        l1 = luminance(fg)
        l2 = luminance(bg)
        lighter = max(l1, l2)
        darker = min(l1, l2)
        return (lighter + 0.05) / (darker + 0.05)

    color = _parse_hex(hex_color)
    background = (color.r, color.g, color.b)
    black = (0.0, 0.0, 0.0)
    white = (1.0, 1.0, 1.0)
    black_ratio = ratio(black, background)
    white_ratio = ratio(white, background)

    if black_ratio >= target_ratio or white_ratio >= target_ratio:
        return "#000000" if black_ratio >= white_ratio else "#FFFFFF"
    return "#000000" if black_ratio >= white_ratio else "#FFFFFF"


__all__ = [
    "hex_to_hsl",
    "hsl_to_hex",
    "lighten",
    "darken",
    "hsl_rotate",
    "desaturate",
    "contrast_color",
]
