"""Engine registry and factory for audio capture engines."""

from typing import Any, Dict, List, Optional, Type

from wled_controller.core.audio.base import AudioCaptureEngine, AudioCaptureStreamBase
from wled_controller.config import is_demo_mode
from wled_controller.utils import get_logger

logger = get_logger(__name__)


class AudioEngineRegistry:
    """Registry for available audio capture engines.

    Maintains a registry of all audio engine implementations
    and provides factory methods for creating capture streams.
    """

    _engines: Dict[str, Type[AudioCaptureEngine]] = {}

    @classmethod
    def register(cls, engine_class: Type[AudioCaptureEngine]):
        """Register an audio capture engine.

        Args:
            engine_class: Engine class to register (must inherit from AudioCaptureEngine)

        Raises:
            ValueError: If engine_class is not a subclass of AudioCaptureEngine
        """
        if not issubclass(engine_class, AudioCaptureEngine):
            raise ValueError(f"{engine_class} must be a subclass of AudioCaptureEngine")

        engine_type = engine_class.ENGINE_TYPE
        if engine_type == "base":
            raise ValueError("Cannot register base engine type")

        if engine_type in cls._engines:
            logger.warning(f"Audio engine '{engine_type}' already registered, overwriting")

        cls._engines[engine_type] = engine_class
        logger.info(f"Registered audio engine: {engine_type}")

    @classmethod
    def get_engine(cls, engine_type: str) -> Type[AudioCaptureEngine]:
        """Get engine class by type.

        Args:
            engine_type: Engine type identifier (e.g., "wasapi", "sounddevice")

        Returns:
            Engine class

        Raises:
            ValueError: If engine type not found
        """
        if engine_type not in cls._engines:
            available = ", ".join(cls._engines.keys()) or "none"
            raise ValueError(
                f"Unknown audio engine type: '{engine_type}'. Available engines: {available}"
            )
        return cls._engines[engine_type]

    @classmethod
    def get_available_engines(cls) -> List[str]:
        """Get list of available engine types on this system.

        Returns:
            List of engine type identifiers that are available
        """
        demo = is_demo_mode()
        available = []
        for engine_type, engine_class in cls._engines.items():
            try:
                # In demo mode, only demo engines are available
                if demo and engine_type != "demo":
                    continue
                if engine_class.is_available():
                    available.append(engine_type)
            except Exception as e:
                logger.error(
                    f"Error checking availability for audio engine '{engine_type}': {e}"
                )
        return available

    @classmethod
    def get_best_available_engine(cls) -> Optional[str]:
        """Get the highest-priority available engine type.

        Returns:
            Engine type string, or None if no engines are available.
        """
        demo = is_demo_mode()
        best_type = None
        best_priority = -1
        for engine_type, engine_class in cls._engines.items():
            try:
                if demo and engine_type != "demo":
                    continue
                if engine_class.is_available() and engine_class.ENGINE_PRIORITY > best_priority:
                    best_priority = engine_class.ENGINE_PRIORITY
                    best_type = engine_type
            except Exception as e:
                logger.error(
                    f"Error checking availability for audio engine '{engine_type}': {e}"
                )
        return best_type

    @classmethod
    def get_all_engines(cls) -> Dict[str, Type[AudioCaptureEngine]]:
        """Get all registered engines (available or not).

        In demo mode, only demo engines are returned.

        Returns:
            Dictionary mapping engine type to engine class
        """
        if is_demo_mode():
            return {k: v for k, v in cls._engines.items() if k == "demo"}
        return cls._engines.copy()

    @classmethod
    def create_stream(
        cls,
        engine_type: str,
        device_index: int,
        is_loopback: bool,
        config: Dict[str, Any],
    ) -> AudioCaptureStreamBase:
        """Create an AudioCaptureStreamBase for the specified engine and device.

        Args:
            engine_type: Engine type identifier
            device_index: Audio device index
            is_loopback: Whether to capture loopback audio
            config: Engine-specific configuration

        Returns:
            Uninitialized AudioCaptureStreamBase instance

        Raises:
            ValueError: If engine type not found or not available
        """
        engine_class = cls.get_engine(engine_type)

        if not engine_class.is_available():
            raise ValueError(
                f"Audio engine '{engine_type}' is not available on this system"
            )

        try:
            stream = engine_class.create_stream(device_index, is_loopback, config)
            logger.debug(
                f"Created audio stream: {engine_type} "
                f"(device={device_index}, loopback={is_loopback})"
            )
            return stream
        except Exception as e:
            logger.error(f"Failed to create stream for audio engine '{engine_type}': {e}")
            raise RuntimeError(
                f"Failed to create stream for audio engine '{engine_type}': {e}"
            )

    @classmethod
    def clear_registry(cls):
        """Clear all registered engines (for testing)."""
        cls._engines.clear()
        logger.debug("Cleared audio engine registry")
