#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Violin plot helper functions for RecordingAxes."""

from typing import Any, Dict, List, Optional

import numpy as np


def add_violin_inner_box(
    ax,
    dataset: List,
    positions: List,
    style: Dict[str, Any],
) -> None:
    """Add box plot inside violin.

    Draws a gray-filled IQR box with whiskers and a black median line,
    consistent with publication-quality scientific figure standards.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes to draw on.
    dataset : array-like
        Data arrays for each violin.
    positions : array-like
        X positions of violins.
    style : dict
        Violin style configuration.
    """
    from matplotlib.patches import Rectangle

    from ..styles._style_applier import mm_to_pt

    whisker_lw = mm_to_pt(style.get("whisker_mm", 0.2))
    median_lw = mm_to_pt(style.get("median_line_mm", 0.2))  # Line thickness
    box_edge_lw = mm_to_pt(style.get("box_edge_mm", 0.2))  # Box outline

    # Use a fraction of position spacing for box width in data coordinates
    box_half_width = 0.06  # Half-width in data coordinates

    for data, pos in zip(dataset, positions):
        data = np.asarray(data)
        q1, median, q3 = np.percentile(data, [25, 50, 75])
        iqr = q3 - q1
        whisker_low = max(data.min(), q1 - 1.5 * iqr)
        whisker_high = min(data.max(), q3 + 1.5 * iqr)

        # Draw gray-filled IQR box
        box_rect = Rectangle(
            (pos - box_half_width, q1),
            box_half_width * 2,
            q3 - q1,
            facecolor="gray",
            edgecolor="black",
            linewidth=box_edge_lw,
            zorder=3,
        )
        ax.add_patch(box_rect)

        # Draw whiskers - thin black lines
        ax.vlines(
            pos,
            whisker_low,
            q1,
            colors="black",
            linewidths=whisker_lw,
            zorder=3,
        )
        ax.vlines(
            pos,
            q3,
            whisker_high,
            colors="black",
            linewidths=whisker_lw,
            zorder=3,
        )

        # Draw median as a black horizontal line (not a dot)
        ax.hlines(
            median,
            pos - box_half_width,
            pos + box_half_width,
            colors="black",
            linewidths=median_lw,
            zorder=4,
        )


def add_violin_inner_swarm(
    ax,
    dataset: List,
    positions: List,
    style: Dict[str, Any],
    colors: Optional[List] = None,
) -> None:
    """Add swarm points inside violin.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes to draw on.
    dataset : array-like
        Data arrays for each violin.
    positions : array-like
        X positions of violins.
    style : dict
        Violin style configuration.
    colors : list, optional
        Per-group colors. Falls back to palette or black.
    """
    import matplotlib.pyplot as mpl_plt

    from ..styles._style_applier import mm_to_pt

    point_size = mm_to_pt(style.get("swarm_size_mm", 0.6))
    swarm_alpha = style.get("swarm_alpha", 0.6)
    jitter_spread = style.get("swarm_jitter", 0.04)

    if colors is None:
        colors = mpl_plt.rcParams["axes.prop_cycle"].by_key()["color"]

    rng = np.random.default_rng(42)
    for i, (data, pos) in enumerate(zip(dataset, positions)):
        data = np.asarray(data)
        x_jitter = rng.normal(pos, jitter_spread, size=len(data))
        color = colors[i % len(colors)]
        ax.scatter(
            x_jitter,
            data,
            s=point_size**2,
            color=color,
            alpha=swarm_alpha,
            zorder=10,
        )


def add_violin_inner_stick(
    ax,
    dataset: List,
    positions: List,
    style: Dict[str, Any],
) -> None:
    """Add stick (line) markers inside violin for each data point.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes to draw on.
    dataset : array-like
        Data arrays for each violin.
    positions : array-like
        X positions of violins.
    style : dict
        Violin style configuration.
    """
    from ..styles._style_applier import mm_to_pt

    lw = mm_to_pt(style.get("whisker_mm", 0.2))

    for data, pos in zip(dataset, positions):
        data = np.asarray(data)
        # Draw short horizontal lines at each data point
        for val in data:
            ax.hlines(
                val,
                pos - 0.05,
                pos + 0.05,
                colors="black",
                linewidths=lw * 0.5,
                alpha=0.3,
                zorder=3,
            )


def add_violin_inner_point(
    ax,
    dataset: List,
    positions: List,
    style: Dict[str, Any],
) -> None:
    """Add point markers inside violin for each data point.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes to draw on.
    dataset : array-like
        Data arrays for each violin.
    positions : array-like
        X positions of violins.
    style : dict
        Violin style configuration.
    """
    from ..styles._style_applier import mm_to_pt

    point_size = mm_to_pt(style.get("median_mm", 0.8)) * 0.5

    for data, pos in zip(dataset, positions):
        data = np.asarray(data)
        x_positions = np.full_like(data, pos)
        ax.scatter(x_positions, data, s=point_size**2, c="black", alpha=0.3, zorder=3)


__all__ = [
    "add_violin_inner_box",
    "add_violin_inner_swarm",
    "add_violin_inner_stick",
    "add_violin_inner_point",
]

# EOF
