"""Informed Kinetics — matplotlib brand helpers.

Companion to ik-matplotlib.mplstyle / ik-matplotlib-dark.mplstyle. A .mplstyle
file can carry the categorical cycle, fonts and chart furniture, but not the
sequential/diverging colormaps or named tokens — those live here.

Usage
-----
    import matplotlib.pyplot as plt
    import ik_brand_mpl as ik

    ik.use("paper")          # or "ink" for the dark surface
    ik.register_colormaps()  # registers "IK-Green", "IK-Diverging" (+ _r)

    plt.plot(x, y)                       # auto-styled, brand colours in order
    plt.imshow(z, cmap="IK-Green")       # single-hue sequential
    plt.axhline(thr, **ik.THRESHOLD)     # dashed danger reference line

Everything is sourced from the IK brand guidelines and matches ik-brand.js.
"""

from __future__ import annotations

import glob
import os

import matplotlib.pyplot as plt
from matplotlib import font_manager
from matplotlib.colors import LinearSegmentedColormap

_HERE = os.path.dirname(os.path.abspath(__file__))

# ---- Typefaces -----------------------------------------------------------
# Headings/display = Mozilla Text; body/UI = IBM Plex Sans; data/labels = mono.
HEADING_FONT = "Mozilla Text"
BODY_FONT = "IBM Plex Sans"
MONO_FONT = "IBM Plex Mono"

# ---- Named brand tokens --------------------------------------------------
INK = "#1C1C1C"
PAPER = "#FFFFFF"
LAVENDER = "#8884FF"
GREEN = "#1C7C54"
MINT = "#73E2A7"

# ---- Categorical series (use strictly in order) --------------------------
# 1 Lavender · 2 Green · 3 Amber · 4 Mint · 5 Slate · 6 Teal · 7 Rose · 8 Brown
# Category 5 is slate-blue — never the danger red — so a series can't be
# misread as "error" in a clinical figure.
CATEGORICAL = [
    "#8884FF",  # 1 lavender
    "#1C7C54",  # 2 green
    "#E0A24A",  # 3 amber
    "#73E2A7",  # 4 mint
    "#3D5A9E",  # 5 slate
    "#2F8E8E",  # 6 teal
    "#C76590",  # 7 rose
    "#94653E",  # 8 brown
]

# ---- Continuous ramps ----------------------------------------------------
# Sequential (heatmaps, intensity) — single-hue green, low -> high.
SEQUENTIAL = ["#0E3D2A", "#1C7C54", "#44B07C", "#73E2A7", "#BFF1D6"]
# Diverging (change-from-baseline) — lavender <- neutral -> green.
DIVERGING = [
    "#4A3FB0", "#8884FF", "#CFCDDE",
    "#EDEBE6",
    "#CDE7D8", "#44B07C", "#15633F",
]

# ---- Danger / reference-line token ---------------------------------------
# Reserved for thresholds & reference lines only — never a data series.
DANGER = "#D6534F"          # on paper
DANGER_ON_INK = "#EE8A86"   # on ink
THRESHOLD = {"color": DANGER, "linestyle": "--", "linewidth": 1.5}
THRESHOLD_ON_INK = {"color": DANGER_ON_INK, "linestyle": "--", "linewidth": 1.5}

# Colour-blind safety: pair colour with distinct marker shapes when a misread
# matters (green/amber/mint is the weakest trio under red-green CVD).
MARKERS = ["o", "s", "^", "D", "v", "P", "X", "*"]

_STYLES = {
    "paper": os.path.join(_HERE, "ik-matplotlib.mplstyle"),
    "ink": os.path.join(_HERE, "ik-matplotlib-dark.mplstyle"),
}


def use(surface: str = "paper") -> None:
    """Apply the IK style. surface = 'paper' (light) or 'ink' (dark)."""
    try:
        path = _STYLES[surface]
    except KeyError:
        raise ValueError("surface must be 'paper' or 'ink'") from None
    plt.style.use(path)


def register_fonts(font_dir: str) -> list[str]:
    """Register every .ttf/.otf in font_dir with matplotlib so the brand
    typefaces (Mozilla Text, IBM Plex) resolve without a system install.
    Returns the family names now available. Call before use()."""
    added = set()
    for path in sorted(glob.glob(os.path.join(font_dir, "*.ttf"))
                       + glob.glob(os.path.join(font_dir, "*.otf"))):
        font_manager.fontManager.addfont(path)
        added.add(font_manager.FontProperties(fname=path).get_name())
    return sorted(added)


def register_colormaps() -> None:
    """Register 'IK-Green', 'IK-Diverging' (and reversed *_r) colormaps."""
    cmaps = {
        "IK-Green": SEQUENTIAL,
        "IK-Diverging": DIVERGING,
    }
    for name, colors in cmaps.items():
        cmap = LinearSegmentedColormap.from_list(name, colors)
        _register(name, cmap)
        _register(name + "_r", cmap.reversed())


def _register(name, cmap):
    try:  # matplotlib >= 3.5
        if name not in plt.colormaps():
            plt.colormaps.register(cmap, name=name)
    except AttributeError:  # older matplotlib
        import matplotlib.cm as cm

        cm.register_cmap(name=name, cmap=cmap)


# Backwards-friendly alias
register_ik_colormaps = register_colormaps
