"""Beat Gate audio filter — pass signal only around beat events."""

import time
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 BeatGateFilter(AudioFilter):
    """Pass audio signal through only when a beat is detected.

    When a beat is detected, the gate opens and holds for ``hold_ms``
    milliseconds, passing the signal through.  Between beats (after hold
    expires), rms/peak/spectrum are zeroed out.  Beat fields themselves
    always pass through unchanged.
    """

    filter_id = "beat_gate"
    filter_name = "Beat Gate"

    def __init__(self, options: Dict[str, Any]):
        super().__init__(options)
        self._hold_ms = self.options["hold_ms"]
        self._last_beat_time: float | None = None

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

    def reset(self) -> None:
        self._last_beat_time = None

    @classmethod
    def get_options_schema(cls) -> List[AudioFilterOptionDef]:
        return [
            AudioFilterOptionDef(
                key="hold_ms",
                label="Hold Time (ms)",
                option_type="float",
                default=50.0,
                min_value=10.0,
                max_value=500.0,
                step=1.0,
            ),
        ]

    def process(self, analysis: AudioAnalysis) -> AudioAnalysis:
        now = time.perf_counter()

        # Record beat time
        if analysis.beat:
            self._last_beat_time = now

        # Check if we're within the hold window
        if self._last_beat_time is not None:
            elapsed_ms = (now - self._last_beat_time) * 1000.0
            if elapsed_ms <= self._hold_ms:
                return analysis

        # Gate closed — zero out levels, preserve beat fields
        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),
        )
