"""Palette analysis helpers."""

from __future__ import annotations

import math
from statistics import mean
from typing import Iterable, Sequence

from .models import Color
from .parser import parse_color

MAX_OKLAB_DISTANCE = 0.5  # Empirical scale factor for "complexity" score


def summarize_palette(values: Iterable[str | Color]) -> dict[str, object]:
    """Return statistics for a collection of colors."""

    colours: list[Color] = []
    palette_all: list[str] = []
    for value in values:
        colour = value if isinstance(value, Color) else parse_color(value)
        if colour is None:
            continue
        colours.append(colour)
        palette_all.append(colour.to_hex())

    unique = list(dict.fromkeys(palette_all))
    stats: dict[str, object] = {
        "palette": unique,
        "palette_all": palette_all,
        "count": len(colours),
        "unique": len(unique),
        "has_transparency": any(c.a < 1.0 for c in colours),
    }

    if not colours:
        stats.update(
            {
                "mean_oklab": (0.0, 0.0, 0.0),
                "variance_oklab": 0.0,
                "max_oklab_distance": 0.0,
                "complexity": 0.0,
            }
        )
        return stats

    lab_values = [c.to_oklab() for c in colours]
    mean_lab = tuple(mean(channel) for channel in zip(*lab_values))
    variance = _mean_squared_distance(lab_values, mean_lab)
    max_distance = _max_pairwise_distance(lab_values)

    stats.update(
        {
            "mean_oklab": mean_lab,
            "variance_oklab": variance,
            "max_oklab_distance": max_distance,
            "complexity": min(1.0, max_distance / MAX_OKLAB_DISTANCE),
        }
    )

    return stats


def _mean_squared_distance(
    values: Sequence[tuple[float, float, float]], mean_value: tuple[float, float, float]
) -> float:
    total = 0.0
    for entry in values:
        total += _oklab_distance(entry, mean_value) ** 2
    return total / len(values)


def _max_pairwise_distance(values: Sequence[tuple[float, float, float]]) -> float:
    max_distance = 0.0
    for index, first in enumerate(values):
        for second in values[index + 1 :]:
            max_distance = max(max_distance, _oklab_distance(first, second))
    return max_distance


def _oklab_distance(
    first: tuple[float, float, float], second: tuple[float, float, float]
) -> float:
    return math.sqrt(sum((a - b) ** 2 for a, b in zip(first, second)))


__all__ = ["summarize_palette", "MAX_OKLAB_DISTANCE"]
