"""Check pairwise colour separability in CIE Lab."""

from itertools import combinations
from math import sqrt

from a11yviz.simulate_cvd import simulate_cvd


def check_separability(colors,
                       min_delta_e: float = 10,
                       type: str = "normal") -> list[dict]:
    """Flag colour pairs that are too similar to tell apart.

    For every pair, computes a perceptual distance and flags pairs that
    fall below ``min_delta_e``. Optional ``type`` argument applies a
    vision-simulation transform before measuring distance.
    """
    if type != "normal":
        colors = simulate_cvd(colors, type=type)
    labs = [_rgb_to_lab(_hex_to_srgb(c)) for c in colors]
    out = []
    for i, j in combinations(range(len(colors)), 2):
        d = sqrt(sum((labs[i][k] - labs[j][k]) ** 2 for k in range(3)))
        out.append({
            "from":    colors[i],
            "to":      colors[j],
            "delta_e": round(d, 2),
            "status":  "ok" if d >= min_delta_e else "todo",
        })
    return out


def _hex_to_srgb(hex_color):
    h = hex_color.lstrip("#")
    return [int(h[i:i + 2], 16) / 255 for i in (0, 2, 4)]


def _to_linear(c):
    return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4


def _rgb_to_lab(rgb):
    lin = [_to_linear(c) for c in rgb]
    m = ((0.4124564, 0.3575761, 0.1804375),
         (0.2126729, 0.7151522, 0.0721750),
         (0.0193339, 0.1191920, 0.9503041))
    x = m[0][0] * lin[0] + m[0][1] * lin[1] + m[0][2] * lin[2]
    y = m[1][0] * lin[0] + m[1][1] * lin[1] + m[1][2] * lin[2]
    z = m[2][0] * lin[0] + m[2][1] * lin[1] + m[2][2] * lin[2]
    fx, fy, fz = _f(x / 0.95047), _f(y / 1.0), _f(z / 1.08883)
    return (116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz))


def _f(t):
    delta = 6 / 29
    return t ** (1 / 3) if t > delta ** 3 else t / (3 * delta ** 2) + 4 / 29
