"""Callback functions for HoloViews DynamicMaps."""

from typing import Annotated, Literal

import holoviews as hv
import numpy as np


def wave_function(
    self,
    wave_type: Literal["sin", "cos", "tan"] = "sin",
    frequency: Annotated[float, "[1,6]"] = 1.0,
    amplitude: Annotated[float, "[3,9]"] = 3.0,
    phase: Annotated[float, "[-3.14159,3.14159]"] = 0.0,
):
    """
    Generate a sine, cosine, or tangent wave plot.

    Wave type: {wave_type}
    Frequency: {frequency} Hz
    Amplitude: {amplitude}
    Phase: {phase} radians
    """
    x = np.linspace(0, 4 * np.pi, 1000)
    wave_func = getattr(np, wave_type)
    y = amplitude * wave_func(frequency * x + phase)
    return hv.Curve((x, y), kdims="x", vdims="y").opts(
        responsive=True,
        height=400,
        line_width=2,
        framewise=True,
        hooks=[self.bokeh_hooks.hook_no_logo, self.bokeh_hooks.horizontal_grid],
    )


def scatter_plot(
    self,
    n_points: Annotated[int, "[50,500]"] = 100,
    noise: Annotated[float, "[0,2]"] = 0.1,
):
    """
    Generate a scatter plot with controllable noise.

    Number of points: {n_points}
    Noise level: {noise}
    """
    x = np.linspace(0, 10, n_points)
    y = x + noise * np.random.randn(n_points)
    return hv.Scatter((x, y), kdims="x", vdims="y").opts(
        responsive=True,
        height=400,
        size=5,
        framewise=True,
        hooks=[self.bokeh_hooks.hook_no_logo, self.bokeh_hooks.fine_horizontal_grid],
    )


def parametric_curve(
    self,
    t_max: Annotated[float, "[1,5]"] = 3.0,
    scale: Annotated[float, "[0.5,3]"] = 1.0,
):
    """
    Generate a parametric curve (Lissajous figure).

    Time range: 0 to {t_max}
    Scale: {scale}
    """
    t = np.linspace(0, t_max, 1000)
    x = scale * np.sin(3 * t)
    y = scale * np.cos(5 * t)
    return hv.Curve((x, y), kdims="x", vdims="y").opts(
        responsive=True,
        height=400,
        line_width=2,
        framewise=True,
        hooks=[
            self.bokeh_hooks.hook_no_logo,
            self.bokeh_hooks.horizontal_grid,
            self.bokeh_hooks.vertical_grid,
        ],
    )


def histogram_plot(
    self,
    n_samples: Annotated[int, "[100,5000]"] = 1000,
    bins: Annotated[int, "[10,100]"] = 30,
):
    """
    Generate a histogram of normally distributed data.

    Sample size: {n_samples}
    Number of bins: {bins}
    """
    data = np.random.randn(n_samples)
    frequencies, edges = np.histogram(data, bins)
    return hv.Histogram((edges, frequencies)).opts(
        responsive=True,
        height=400,
        framewise=True,
        hooks=[self.bokeh_hooks.hook_no_logo, self.bokeh_hooks.horizontal_grid],
    )


def heatmap_plot(
    self, size: Annotated[int, "[10,50]"] = 20, noise: Annotated[float, "[0,2]"] = 0.5
):
    """
    Generate a heatmap with random data.

    Grid size: {size} x {size}
    Noise level: {noise}
    """
    x, y = np.meshgrid(np.arange(size), np.arange(size))
    z = np.sin(x / 2) * np.cos(y / 2) + noise * np.random.randn(size, size)
    return hv.HeatMap((x.flatten(), y.flatten(), z.flatten())).opts(
        responsive=True,
        height=400,
        colorbar=True,
        framewise=True,
        hooks=[self.bokeh_hooks.hook_no_logo, self.bokeh_hooks.hook_no_toolbar],
    )


def bar_chart(
    self,
    n_bars: Annotated[int, "[5,20]"] = 10,
    color: Literal["steelblue", "coral", "green", "purple", "orange"] = "steelblue",
    show_values: bool = True,
):
    """
    Generate a bar chart with customizable appearance.

    Number of bars: {n_bars}
    Bar color: {color}
    Show values: {show_values}
    """
    # Use seed based on n_bars to get consistent values
    np.random.seed(n_bars * 42)
    categories = [f"Cat {i+1}" for i in range(n_bars)]
    values = np.random.randint(10, 100, n_bars)

    bars = hv.Bars((categories, values), kdims="Category", vdims="Value").opts(
        responsive=True,
        height=400,
        color=color,
        framewise=True,
        hooks=[self.bokeh_hooks.hook_no_logo, self.bokeh_hooks.horizontal_grid],
    )

    # Create labels but control visibility with alpha
    label_alpha = 1.0 if show_values else 0.0
    labels = hv.Labels(
        [(cat, val, str(val)) for cat, val in zip(categories, values)],
        kdims=["Category", "Value"],
        vdims=["text"],
    ).opts(
        text_baseline="bottom",
        text_align="center",
        text_font_size="8pt",
        text_alpha=label_alpha,
    )

    return (bars * labels).opts(responsive=True, height=400)


def spiral_plot(
    self,
    rotations: Annotated[float, "[1,10]"] = 3.0,
    plot_title: str = "Spiral Pattern",
    invert: bool = False,
):
    """
    Generate a spiral curve with customizable parameters.

    Rotations: {rotations}
    Plot title: {plot_title}
    Inverted: {invert}
    """
    theta = np.linspace(0, rotations * 2 * np.pi, 1000)
    r = theta
    if invert:
        r = rotations * 2 * np.pi - theta
    x = r * np.cos(theta)
    y = r * np.sin(theta)

    return hv.Curve((x, y), kdims="x", vdims="y").opts(
        responsive=True,
        height=400,
        line_width=2,
        framewise=True,
        title=plot_title,
        color="purple",
        hooks=[
            self.bokeh_hooks.hook_no_logo,
            self.bokeh_hooks.horizontal_grid,
            self.bokeh_hooks.vertical_grid,
        ],
    )
