"""Audio filter pipeline — chains multiple AudioFilter instances sequentially.

The pipeline is the runtime executor for audio processing template chains.
It takes a flat list of FilterInstance specs, instantiates each filter via
the AudioFilterRegistry, and runs AudioAnalysis through them in order.

Thread-safe: each stream gets its own pipeline instance with independent
stateful filter state.
"""

from __future__ import annotations

import threading
from typing import TYPE_CHECKING, List

from wled_controller.core.audio.analysis import AudioAnalysis
from wled_controller.core.audio.filters.base import AudioFilter
from wled_controller.core.audio.filters.registry import AudioFilterRegistry
from wled_controller.utils import get_logger

if TYPE_CHECKING:
    from wled_controller.core.filters.filter_instance import FilterInstance
    from wled_controller.storage.audio_processing_template_store import (
        AudioProcessingTemplateStore,
    )

logger = get_logger(__name__)


class AudioFilterPipeline:
    """Chains multiple AudioFilter instances and runs AudioAnalysis through them.

    Each pipeline owns its own filter instances — stateful filters maintain
    per-pipeline state, so pipelines must NOT be shared across streams.
    """

    def __init__(self, filter_instances: List["FilterInstance"]) -> None:
        """Create a pipeline from FilterInstance specs.

        Args:
            filter_instances: Flat (already-resolved) list of FilterInstance specs.
                              Each is instantiated via AudioFilterRegistry.create_instance().
        """
        self._lock = threading.Lock()
        self._filters: List[AudioFilter] = []
        for fi in filter_instances:
            try:
                f = AudioFilterRegistry.create_instance(fi.filter_id, fi.options)
                self._filters.append(f)
            except ValueError:
                logger.warning("Skipping unknown audio filter '%s'", fi.filter_id)

    @property
    def empty(self) -> bool:
        """True when the pipeline has no filters (passthrough)."""
        return len(self._filters) == 0

    def process(self, analysis: AudioAnalysis) -> AudioAnalysis:
        """Run analysis through all filters in order.

        Returns a new AudioAnalysis (filters produce new objects via
        dataclasses.replace, never mutating the input).
        """
        result = analysis
        with self._lock:
            for f in self._filters:
                result = f.process(result)
        return result

    def reset(self) -> None:
        """Reset all stateful filters to their initial state."""
        with self._lock:
            for f in self._filters:
                if f.is_stateful:
                    f.reset()

    def close(self) -> None:
        """Release resources and clear filter list."""
        with self._lock:
            self._filters.clear()

    def __repr__(self) -> str:
        return f"AudioFilterPipeline(filters={self._filters})"


def build_pipeline_from_template_ids(
    template_ids: List[str],
    template_store: "AudioProcessingTemplateStore",
) -> AudioFilterPipeline:
    """Resolve a list of audio processing template IDs into a single pipeline.

    Expands each template's filters (including nested audio_filter_template
    references) via the store's resolve_filter_instances(), concatenates all
    resolved FilterInstance lists in order, and returns a ready-to-use pipeline.

    Args:
        template_ids: Ordered template IDs (outermost first, as returned by
                      ResolvedAudioSource.audio_processing_template_ids).
        template_store: AudioProcessingTemplateStore for template lookup and
                        recursive filter resolution.

    Returns:
        AudioFilterPipeline (may be empty if no templates / all templates missing).
    """
    all_instances: List[FilterInstance] = []
    for tid in template_ids:
        try:
            template = template_store.get_template(tid)
            resolved = template_store.resolve_filter_instances(template.filters)
            all_instances.extend(resolved)
        except ValueError:
            logger.warning("Audio processing template '%s' not found, skipping", tid)
    return AudioFilterPipeline(all_instances)
