"""Outils de tri de couleurs dans un espace perceptuel."""

import math
from typing import Iterable, List, Tuple

import numpy as np
from colorspacious import cspace_convert


def hex_to_rgb_unit(hex_value: str) -> np.ndarray:
    """Convertit un code hexadécimal en tableau RGB normalisé (0-1)."""
    return np.array([int(hex_value[index : index + 2], 16) / 255 for index in (0, 2, 4)], dtype=float)


def lab_components(hex_value: str) -> Tuple[float, float, float, float, float]:
    """Retourne (hue_angle, chroma, lightness, a*, b*) pour une couleur."""
    l_component, a_component, b_component = cspace_convert(hex_to_rgb_unit(hex_value), "sRGB1", "CIELab")
    hue_angle = math.atan2(b_component, a_component)
    chroma = math.hypot(a_component, b_component)
    return hue_angle, chroma, l_component, a_component, b_component


def sort_hex_colors_lab(
    hex_values: Iterable[str],
    hue_offset_degrees: float = 60.0,
    neutral_threshold: float = 3.0,
) -> List[str]:
    """
    Trie des couleurs par teinte perceptuelle, puis chroma et luminosité.

    - Les couleurs quasi neutres (chroma < seuil) sont déplacées en fin de liste, triées par luminosité.
    - Le cercle chromatique peut être décalé via hue_offset_degrees (par défaut 60° pour démarrer vers le jaune).
    """
    offset_radians = math.radians(hue_offset_degrees)
    chromatic: List[Tuple[float, float, float, str]] = []
    neutrals: List[Tuple[float, str]] = []
    for hex_value in hex_values:
        hue_angle, chroma, lightness, _, _ = lab_components(hex_value)
        if chroma < neutral_threshold:
            neutrals.append((lightness, hex_value))
            continue
        hue = hue_angle + offset_radians
        if hue < 0:
            hue += 2 * math.pi
        chromatic.append((hue, -chroma, lightness, hex_value))
    chromatic.sort()
    neutrals.sort()
    return [item[3] for item in chromatic] + [item[1] for item in neutrals]


def lab_sort_key(hex_value: str, hue_offset_degrees: float = 60.0, neutral_threshold: float = 3.0) -> Tuple[int, float, float, float]:
    """Clé de tri unique (bucket chromatique/neutre) pour un usage ponctuel."""
    hue_angle, chroma, lightness, _, _ = lab_components(hex_value)
    if chroma < neutral_threshold:
        return (1, 0.0, lightness, chroma)
    hue = hue_angle + math.radians(hue_offset_degrees)
    if hue < 0:
        hue += 2 * math.pi
    return (0, hue, -chroma, lightness)
