"""Spectral Smoothing audio filter — exponential moving average per spectrum bin."""

from dataclasses import replace
from typing import Any, Dict, List

import numpy as np

from wled_controller.core.audio.analysis import NUM_BANDS, AudioAnalysis
from wled_controller.core.audio.filters.base import AudioFilter, AudioFilterOptionDef
from wled_controller.core.audio.filters.registry import AudioFilterRegistry


@AudioFilterRegistry.register
class SpectralSmoothingFilter(AudioFilter):
    """Apply exponential moving average smoothing to each spectrum bin.

    ``smoothed[i] = factor * prev[i] + (1 - factor) * current[i]``

    Higher factor values produce smoother (slower-responding) output.
    """

    filter_id = "spectral_smoothing"
    filter_name = "Spectral Smoothing"

    def __init__(self, options: Dict[str, Any]):
        super().__init__(options)
        self._factor = self.options["factor"]
        self._prev_spectrum = np.zeros(NUM_BANDS, dtype=np.float32)
        self._prev_left = np.zeros(NUM_BANDS, dtype=np.float32)
        self._prev_right = np.zeros(NUM_BANDS, dtype=np.float32)

    @property
    def is_stateful(self) -> bool:
        return True

    def reset(self) -> None:
        self._prev_spectrum[:] = 0.0
        self._prev_left[:] = 0.0
        self._prev_right[:] = 0.0

    @classmethod
    def get_options_schema(cls) -> List[AudioFilterOptionDef]:
        return [
            AudioFilterOptionDef(
                key="factor",
                label="Smoothing Factor",
                option_type="float",
                default=0.5,
                min_value=0.0,
                max_value=0.99,
                step=0.01,
            ),
        ]

    def _smooth(self, prev: np.ndarray, current: np.ndarray) -> np.ndarray:
        """Compute EMA and update previous state in-place, returning a copy."""
        f = self._factor
        smoothed = f * prev + (1.0 - f) * current
        np.copyto(prev, smoothed)
        return smoothed.astype(np.float32)

    def process(self, analysis: AudioAnalysis) -> AudioAnalysis:
        new_spectrum = self._smooth(self._prev_spectrum, analysis.spectrum)
        new_left = self._smooth(self._prev_left, analysis.left_spectrum)
        new_right = self._smooth(self._prev_right, analysis.right_spectrum)

        return replace(
            analysis,
            spectrum=new_spectrum,
            left_spectrum=new_left,
            right_spectrum=new_right,
        )
