"""Noise Gate audio filter — zero signal below threshold with hysteresis."""

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

_ZERO_SPECTRUM = np.zeros(NUM_BANDS, dtype=np.float32)


@AudioFilterRegistry.register
class NoiseGateFilter(AudioFilter):
    """Zero out all audio levels when RMS falls below a threshold.

    Hysteresis prevents rapid gate toggling: the gate opens when RMS rises
    above ``threshold`` and closes only when RMS drops below
    ``threshold - hysteresis``.
    """

    filter_id = "noise_gate"
    filter_name = "Noise Gate"

    def __init__(self, options: Dict[str, Any]):
        super().__init__(options)
        self._threshold = self.options["threshold"]
        self._hysteresis = self.options["hysteresis"]
        self._gate_open = False

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

    def reset(self) -> None:
        self._gate_open = False

    @classmethod
    def get_options_schema(cls) -> List[AudioFilterOptionDef]:
        return [
            AudioFilterOptionDef(
                key="threshold",
                label="Threshold",
                option_type="float",
                default=0.05,
                min_value=0.0,
                max_value=1.0,
                step=0.01,
            ),
            AudioFilterOptionDef(
                key="hysteresis",
                label="Hysteresis",
                option_type="float",
                default=0.05,
                min_value=0.0,
                max_value=0.2,
                step=0.01,
            ),
        ]

    def process(self, analysis: AudioAnalysis) -> AudioAnalysis:
        rms = analysis.rms

        # Update gate state with hysteresis
        if self._gate_open:
            if rms < (self._threshold - self._hysteresis):
                self._gate_open = False
        else:
            if rms >= self._threshold:
                self._gate_open = True

        if self._gate_open:
            return analysis

        # Gate is closed — zero out levels, preserve beat fields and timestamp
        return replace(
            analysis,
            rms=0.0,
            peak=0.0,
            spectrum=np.copy(_ZERO_SPECTRUM),
            left_rms=0.0,
            left_spectrum=np.copy(_ZERO_SPECTRUM),
            right_rms=0.0,
            right_spectrum=np.copy(_ZERO_SPECTRUM),
        )
