"""Plot styling presets and colorblind-safe palettes.

Provides IEEE styling and accessibility-focused color schemes.

Example:
    >>> from oscura.visualization.styles import apply_ieee_style
    >>> import matplotlib.pyplot as plt
    >>> fig, ax = plt.subplots()
    >>> ax.plot([1, 2, 3])
    >>> apply_ieee_style(ax)
"""

from __future__ import annotations

from typing import Literal

try:
    from matplotlib.axes import Axes

    HAS_MATPLOTLIB = True
except ImportError:
    HAS_MATPLOTLIB = False


# Colorblind-safe palettes (Tol Bright)
COLORBLIND_PALETTE: list[str] = [
    "#4477AA",  # Blue
    "#EE6677",  # Red
    "#228833",  # Green
    "#CCBB44",  # Yellow
    "#66CCEE",  # Cyan
    "#AA3377",  # Purple
    "#BBBBBB",  # Gray
]

# IEEE publication styling defaults
IEEE_STYLE: dict[str, str | float] = {
    "font.family": "serif",
    "font.size": 10,
    "axes.linewidth": 0.8,
    "grid.alpha": 0.3,
    "grid.linestyle": ":",
    "lines.linewidth": 1.0,
    "lines.markersize": 4.0,
    "figure.dpi": 600,
    "savefig.dpi": 600,
    "savefig.bbox": "tight",
}

# Line styles for accessibility (when color alone isn't sufficient)
LINE_STYLES: list[str] = ["solid", "dashed", "dotted", "dashdot"]

# Pass/fail symbols for reports
PASS_SYMBOL = "✓"
FAIL_SYMBOL = "✗"


def apply_ieee_style(ax: Axes) -> None:
    """Apply IEEE publication styling to axes.

    Configures fonts, line widths, and grid for publication-quality plots.

    Args:
        ax: Matplotlib axes to style.

    Example:
        >>> fig, ax = plt.subplots()
        >>> ax.plot(x, y)
        >>> apply_ieee_style(ax)
        >>> fig.savefig("publication.pdf")
    """
    if not HAS_MATPLOTLIB:
        raise ImportError("matplotlib required for styling")

    # Apply IEEE styling
    for spine in ax.spines.values():
        spine.set_linewidth(0.8)

    ax.grid(True, alpha=0.3, linestyle=":")

    # Update tick parameters
    ax.tick_params(width=0.8, labelsize=9)

    # Set font properties
    ax.xaxis.label.set_fontsize(10)
    ax.yaxis.label.set_fontsize(10)


def get_colorblind_palette(n_colors: int | None = None) -> list[str]:
    """Get colorblind-safe color palette.

    Returns perceptually distinct colors that work for colorblind viewers.

    Args:
        n_colors: Number of colors to return (default: all 7).

    Returns:
        List of hex color codes.

    Example:
        >>> colors = get_colorblind_palette(3)
        >>> for i, color in enumerate(colors):
        ...     ax.plot(x, y[i], color=color)
    """
    if n_colors is None:
        return COLORBLIND_PALETTE.copy()
    return COLORBLIND_PALETTE[:n_colors]


def get_colorblind_cmap(name: Literal["viridis", "cividis", "plasma"] = "viridis") -> str:
    """Get colorblind-safe colormap name.

    Args:
        name: Colormap name (viridis, cividis, plasma).

    Returns:
        Matplotlib colormap name.

    Example:
        >>> cmap = get_colorblind_cmap("viridis")
        >>> ax.imshow(data, cmap=cmap)
    """
    valid = {"viridis", "cividis", "plasma"}
    if name not in valid:
        raise ValueError(f"Unknown colormap: {name}. Valid: {valid}")
    return name


def get_line_styles(n_lines: int) -> list[str]:
    """Get distinct line styles for multi-line plots.

    Useful when color alone isn't sufficient (grayscale printing, accessibility).

    Args:
        n_lines: Number of line styles needed.

    Returns:
        List of line style strings.

    Example:
        >>> styles = get_line_styles(4)
        >>> for i, style in enumerate(styles):
        ...     ax.plot(x, y[i], linestyle=style)
    """
    # Cycle through available styles if needed
    return [LINE_STYLES[i % len(LINE_STYLES)] for i in range(n_lines)]


def format_pass_fail(passed: bool) -> str:
    """Format pass/fail status with symbol.

    Args:
        passed: Test result.

    Returns:
        Formatted string with symbol.

    Example:
        >>> status = format_pass_fail(True)
        >>> print(status)  # "✓ PASS"
    """
    return f"{PASS_SYMBOL} PASS" if passed else f"{FAIL_SYMBOL} FAIL"


def apply_presentation_style(ax: Axes) -> None:
    """Apply presentation styling (larger fonts, thicker lines).

    Optimized for slides and projection.

    Args:
        ax: Matplotlib axes to style.

    Example:
        >>> fig, ax = plt.subplots()
        >>> ax.plot(x, y)
        >>> apply_presentation_style(ax)
    """
    if not HAS_MATPLOTLIB:
        raise ImportError("matplotlib required for styling")

    # Thicker lines and larger fonts
    for line in ax.get_lines():
        line.set_linewidth(2.5)

    for spine in ax.spines.values():
        spine.set_linewidth(2.0)

    ax.grid(True, alpha=0.5, linestyle="-", linewidth=1.0)
    ax.tick_params(width=2.0, labelsize=14)
    ax.xaxis.label.set_fontsize(16)
    ax.yaxis.label.set_fontsize(16)
    if ax.get_title():
        ax.title.set_fontsize(18)


def get_multi_line_styles(num_lines: int) -> list[dict[str, str | float]]:
    """Get distinct line styles for multiple traces on one plot.

    Generates unique combinations of colors and line styles to ensure
    visual differentiation, even for colorblind users.

    Args:
        num_lines: Number of distinct line styles needed.

    Returns:
        List of style dictionaries with 'color', 'linestyle', and 'linewidth' keys.

    Example:
        >>> styles = get_multi_line_styles(3)
        >>> for i, style in enumerate(styles):
        ...     ax.plot(x, data[i], **style, label=f"Trace {i}")

    References:
        Tol, P. (2021). Color Schemes. Technical Note SRON/EPS/TN/09-002.
    """
    if not HAS_MATPLOTLIB:
        raise ImportError("matplotlib required for line styles")

    styles: list[dict[str, str | float]] = []
    for i in range(num_lines):
        color_idx = i % len(COLORBLIND_PALETTE)
        linestyle_idx = (i // len(COLORBLIND_PALETTE)) % len(LINE_STYLES)

        style_dict: dict[str, str | float] = {
            "color": COLORBLIND_PALETTE[color_idx],
            "linestyle": LINE_STYLES[linestyle_idx],
            "linewidth": 1.5,
        }
        styles.append(style_dict)

    return styles


__all__ = [
    "COLORBLIND_PALETTE",
    "FAIL_SYMBOL",
    "IEEE_STYLE",
    "LINE_STYLES",
    "PASS_SYMBOL",
    "apply_ieee_style",
    "apply_presentation_style",
    "format_pass_fail",
    "get_colorblind_cmap",
    "get_colorblind_palette",
    "get_line_styles",
    "get_multi_line_styles",
]
