"""Motion blur effect using frame history."""

from engine.effects.types import EffectConfig, EffectContext, EffectPlugin


class MotionBlurEffect(EffectPlugin):
    """Apply motion blur by blending current frame with previous frames.

    This effect requires a FrameBufferStage to be present in the pipeline.
    The framebuffer provides frame history which is blended with the current
    frame based on intensity.

    Attributes:
        name: "motionblur"
        config: EffectConfig with intensity parameter (0.0-1.0)
        param_bindings: Optional sensor bindings for intensity modulation

    Example:
        >>> effect = MotionBlurEffect()
        >>> effect.configure(EffectConfig(intensity=0.5))
        >>> result = effect.process(buffer, ctx)
    """

    name = "motionblur"
    config: EffectConfig = EffectConfig(enabled=True, intensity=0.5)
    param_bindings: dict[str, dict[str, str | float]] = {}
    supports_partial_updates = False

    def process(self, buf: list[str], ctx: EffectContext) -> list[str]:
        """Apply motion blur by blending with previous frames.

        Args:
            buf: Current text buffer (list of strings)
            ctx: Effect context with access to framebuffer history

        Returns:
            Blended buffer with motion blur effect applied
        """
        if not buf:
            return buf

        # Get framebuffer history from context
        # We'll look for the first available framebuffer history
        history = None

        for key in ctx.state:
            if key.startswith("framebuffer.") and key.endswith(".history"):
                history = ctx.state[key]
                break

        if not history:
            # No framebuffer available, return unchanged
            return buf

        # Get intensity from config
        intensity = self.config.params.get("intensity", self.config.intensity)
        intensity = max(0.0, min(1.0, intensity))

        if intensity <= 0.0:
            return buf

        # Get decay factor (how quickly older frames fade)
        decay = self.config.params.get("decay", 0.7)

        # Build output buffer
        result = []
        viewport_height = ctx.terminal_height - ctx.ticker_height

        # Determine how many frames to blend (up to history depth)
        max_frames = min(len(history), 5)  # Cap at 5 frames for performance

        for row in range(len(buf)):
            if row >= viewport_height:
                # Beyond viewport, just copy
                result.append(buf[row])
                continue

            # Start with current frame
            blended = buf[row]

            # Blend with historical frames
            weight_sum = 1.0
            if max_frames > 0 and intensity > 0:
                for i in range(max_frames):
                    frame_weight = intensity * (decay**i)
                    if frame_weight < 0.01:  # Skip negligible weights
                        break

                    hist_row = history[i][row] if row < len(history[i]) else ""
                    # Simple string blending: we'll concatenate with space
                    # For a proper effect, we'd need to blend ANSI colors
                    # This is a simplified version that just adds the frames
                    blended = self._blend_strings(blended, hist_row, frame_weight)
                    weight_sum += frame_weight

            result.append(blended)

        return result

    def _blend_strings(self, current: str, historical: str, weight: float) -> str:
        """Blend two strings with given weight.

        This is a simplified blending that works with ANSI codes.
        For proper blending we'd need to parse colors, but for now
        we use a heuristic: if strings are identical, return one.
        If they differ, we alternate or concatenate based on weight.
        """
        if current == historical:
            return current

        # If weight is high, show current; if low, show historical
        if weight > 0.5:
            return current
        else:
            return historical

    def configure(self, config: EffectConfig) -> None:
        """Configure the effect."""
        self.config = config
