"""Curated sRGB palettes for scientific figures.

Sources were checked 2026-07-23. Palette selection alone never establishes
accessibility: audit the rendered foreground/background contrast and add
redundant encodings.

Paul Tol values and recommended fixed order:
https://sronpersonalpages.nl/~pault/data/colourschemes.pdf
(SRON/EPS/TN/09-002, issue 3.2, 18 August 2021)
"""

from __future__ import annotations

from typing import Final

# Okabe-Ito colors as reproduced in Wong, Nature Methods 8, 441 (2011).
OKABE_ITO: Final = {
    "orange": "#E69F00",
    "sky_blue": "#56B4E9",
    "bluish_green": "#009E73",
    "yellow": "#F0E442",
    "blue": "#0072B2",
    "vermillion": "#D55E00",
    "reddish_purple": "#CC79A7",
    "black": "#000000",
}
OKABE_ITO_LIST: Final = [
    "#E69F00",
    "#56B4E9",
    "#009E73",
    "#F0E442",
    "#0072B2",
    "#D55E00",
    "#CC79A7",
    "#000000",
]

# These five meet 3:1 against white in sRGB. Still use markers/line styles.
OKABE_ITO_ON_WHITE: Final = [
    "#0072B2",
    "#D55E00",
    "#009E73",
    "#CC79A7",
    "#000000",
]

# Compatibility alias: Wong presents the same eight colors in another order.
WONG: Final = [
    "#000000",
    "#E69F00",
    "#56B4E9",
    "#009E73",
    "#F0E442",
    "#0072B2",
    "#D55E00",
    "#CC79A7",
]

# Paul Tol qualitative schemes in the fixed sequence recommended in issue 3.2.
TOL_BRIGHT: Final = [
    "#4477AA",
    "#EE6677",
    "#228833",
    "#CCBB44",
    "#66CCEE",
    "#AA3377",
    "#BBBBBB",
]
TOL_HIGH_CONTRAST: Final = ["#004488", "#DDAA33", "#BB5566"]
TOL_VIBRANT: Final = [
    "#EE7733",
    "#0077BB",
    "#33BBEE",
    "#EE3377",
    "#CC3311",
    "#009988",
    "#BBBBBB",
]
TOL_MUTED: Final = [
    "#CC6677",
    "#332288",
    "#DDCC77",
    "#117733",
    "#88CCEE",
    "#882255",
    "#44AA99",
    "#999933",
    "#AA4499",
]
TOL_MEDIUM_CONTRAST: Final = [
    "#6699CC",
    "#004488",
    "#EECC66",
    "#994455",
    "#997700",
    "#EE99AA",
]
TOL_PALE: Final = [
    "#BBCCEE",
    "#CCEEFF",
    "#CCDDAA",
    "#EEEEBB",
    "#FFCCCC",
    "#DDDDDD",
]
TOL_DARK: Final = [
    "#222255",
    "#225555",
    "#225522",
    "#666633",
    "#663333",
    "#555555",
]
TOL_LIGHT: Final = [
    "#77AADD",
    "#EE8866",
    "#EEDD88",
    "#FFAABB",
    "#99DDFF",
    "#44BB99",
    "#BBCC33",
    "#AAAA00",
    "#DDDDDD",
]

PALETTES: Final = {
    "okabe_ito": OKABE_ITO_LIST,
    "okabe_ito_on_white": OKABE_ITO_ON_WHITE,
    "wong": WONG,
    "tol_bright": TOL_BRIGHT,
    "tol_high_contrast": TOL_HIGH_CONTRAST,
    "tol_vibrant": TOL_VIBRANT,
    "tol_muted": TOL_MUTED,
    "tol_medium_contrast": TOL_MEDIUM_CONTRAST,
    "tol_pale": TOL_PALE,
    "tol_dark": TOL_DARK,
    "tol_light": TOL_LIGHT,
}

PALETTE_METADATA: Final = {
    "okabe_ito": {
        "kind": "qualitative",
        "source": "https://www.nature.com/articles/nmeth.1618",
        "caveat": "Yellow and other light colors need outlines on white.",
    },
    "okabe_ito_on_white": {
        "kind": "qualitative-subset",
        "source": "derived from Okabe-Ito by WCAG sRGB contrast calculation",
        "caveat": "At most five categories; use redundant encodings.",
    },
    "tol_bright": {
        "kind": "qualitative",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "maximum_categories": 7,
    },
    "tol_high_contrast": {
        "kind": "qualitative",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "maximum_categories": 3,
        "caveat": "Designed to retain separation in grayscale.",
    },
    "tol_vibrant": {
        "kind": "qualitative",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "maximum_categories": 7,
    },
    "tol_muted": {
        "kind": "qualitative",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "maximum_categories": 9,
    },
    "tol_medium_contrast": {
        "kind": "qualitative-pairs",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "maximum_categories": 6,
    },
    "tol_pale": {
        "kind": "text-background",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "caveat": "Not intended for plot lines or categorical maps.",
    },
    "tol_dark": {
        "kind": "text-foreground",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "caveat": "Not intended as a multi-category plot palette.",
    },
    "tol_light": {
        "kind": "labeled-cell-fill",
        "source": "https://sronpersonalpages.nl/~pault/data/colourschemes.pdf",
        "maximum_categories": 9,
    },
}

SEQUENTIAL_COLORMAPS: Final = [
    "viridis",
    "plasma",
    "inferno",
    "magma",
    "cividis",
]

# Candidates, not blanket accessibility certifications. Center and normalization
# must match the scientific meaning, and rendered contrast still needs review.
DIVERGING_COLORMAP_CANDIDATES: Final = ["RdBu_r", "PuOr", "BrBG"]
DIVERGING_COLORMAPS_SAFE = DIVERGING_COLORMAP_CANDIDATES  # Compatibility alias.
DIVERGING_COLORMAPS_AVOID: Final = ["RdYlGn"]

FLUOROPHORES_TRADITIONAL: Final = {
    "DAPI": "#0000FF",
    "GFP": "#00FF00",
    "RFP": "#FF0000",
    "Cy5": "#FF00FF",
    "YFP": "#FFFF00",
}
FLUOROPHORES_ACCESSIBLE: Final = {
    "Channel1": "#0072B2",
    "Channel2": "#E69F00",
    "Channel3": "#D55E00",
    "Channel4": "#CC79A7",
    "Channel5": "#F0E442",
}
DNA_BASES: Final = {
    "A": "#00CC00",
    "C": "#0000CC",
    "G": "#FFB300",
    "T": "#CC0000",
}
DNA_BASES_ACCESSIBLE: Final = {
    "A": "#009E73",
    "C": "#0072B2",
    "G": "#E69F00",
    "T": "#D55E00",
}


def get_palette(palette_name: str = "okabe_ito_on_white") -> list[str]:
    """Return a copy of a named palette."""
    try:
        return list(PALETTES[palette_name])
    except KeyError as exc:
        available = ", ".join(sorted(PALETTES))
        raise ValueError(
            f"palette {palette_name!r} not found; available: {available}"
        ) from exc


def apply_palette(palette_name: str = "okabe_ito_on_white") -> list[str]:
    """Apply a named palette to Matplotlib, importing it only when requested."""
    try:
        import matplotlib as mpl
    except ImportError as exc:
        raise RuntimeError(
            "Matplotlib is required to apply a palette; "
            "install the pinned snapshot documented in SKILL.md"
        ) from exc
    colors = get_palette(palette_name)
    mpl.rcParams["axes.prop_cycle"] = mpl.cycler(color=colors)
    return colors


if __name__ == "__main__":
    for palette_name in sorted(PALETTES):
        print(f"{palette_name}: {', '.join(PALETTES[palette_name])}")
