"""Temporal blur postprocessing filter — blends current frame with history."""

from typing import Any, Dict, List, Optional

import numpy as np

from wled_controller.core.filters.base import FilterOptionDef, PostprocessingFilter
from wled_controller.core.filters.image_pool import ImagePool
from wled_controller.core.filters.registry import FilterRegistry


@FilterRegistry.register
class TemporalBlurFilter(PostprocessingFilter):
    """Blends each frame with a running accumulator for motion smoothing.

    Uses exponential moving average: acc = (1 - strength) * frame + strength * acc
    Higher strength = more blur / longer trails.
    """

    filter_id = "temporal_blur"
    filter_name = "Temporal Blur"

    def __init__(self, options: Dict[str, Any]):
        super().__init__(options)
        self._acc: Optional[np.ndarray] = None

    @classmethod
    def get_options_schema(cls) -> List[FilterOptionDef]:
        return [
            FilterOptionDef(
                key="strength",
                label="Strength",
                option_type="float",
                default=0.5,
                min_value=0.0,
                max_value=0.95,
                step=0.05,
            ),
        ]

    def process_image(self, image: np.ndarray, image_pool: ImagePool) -> Optional[np.ndarray]:
        strength = self.options["strength"]
        if strength == 0.0:
            self._acc = None
            return None

        h, w, c = image.shape
        shape = (h, w, c)

        if self._acc is None or self._acc.shape != shape:
            self._acc = image.astype(np.float32)
            return None

        # EMA: acc = strength * acc + (1 - strength) * current
        new_weight = 1.0 - strength
        self._acc *= strength
        self._acc += new_weight * image
        np.copyto(image, self._acc, casting="unsafe")
        return None

    def process_strip(self, strip: np.ndarray) -> Optional[np.ndarray]:
        """Optimized strip path — avoids reshape overhead."""
        strength = self.options["strength"]
        if strength == 0.0:
            self._acc = None
            return None

        shape = strip.shape
        if self._acc is None or self._acc.shape != shape:
            self._acc = strip.astype(np.float32)
            return None

        new_weight = 1.0 - strength
        self._acc *= strength
        self._acc += new_weight * strip
        np.copyto(strip, self._acc, casting="unsafe")
        return None
