"""Demo audio engine — virtual audio devices with synthetic audio data."""

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

import numpy as np

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

logger = get_logger(__name__)

# Virtual audio device definitions: (name, is_loopback, channels, samplerate)
_VIRTUAL_DEVICES = [
    ("Demo Microphone", False, 2, 44100.0),
    ("Demo System Audio", True, 2, 44100.0),
]


class DemoAudioCaptureStream(AudioCaptureStreamBase):
    """Demo audio capture stream that produces synthetic music-like audio.

    Generates a mix of sine waves with slowly varying frequencies to
    simulate beat-like patterns suitable for audio-reactive visualizations.
    """

    def __init__(self, device_index: int, is_loopback: bool, config: Dict[str, Any]):
        super().__init__(device_index, is_loopback, config)
        self._channels = 2
        self._sample_rate = 44100
        self._chunk_size = 1024
        self._phase = 0.0  # Accumulated phase in samples for continuity

    @property
    def channels(self) -> int:
        return self._channels

    @property
    def sample_rate(self) -> int:
        return self._sample_rate

    @property
    def chunk_size(self) -> int:
        return self._chunk_size

    def initialize(self) -> None:
        if self._initialized:
            return
        self._phase = 0.0
        self._initialized = True
        logger.info(
            f"Demo audio stream initialized "
            f"(device={self.device_index}, loopback={self.is_loopback})"
        )

    def cleanup(self) -> None:
        self._initialized = False
        logger.info(f"Demo audio stream cleaned up (device={self.device_index})")

    def read_chunk(self) -> Optional[np.ndarray]:
        if not self._initialized:
            return None

        t_now = time.time()
        n = self._chunk_size
        sr = self._sample_rate

        # Sample indices for this chunk (continuous across calls)
        t = (self._phase + np.arange(n, dtype=np.float64)) / sr
        self._phase += n

        # --- Synthetic "music" signal ---
        # Bass drum: ~80 Hz with slow amplitude envelope (~2 Hz beat)
        bass_freq = 80.0
        beat_rate = 2.0  # beats per second
        bass_env = np.maximum(0.0, np.sin(2.0 * np.pi * beat_rate * t)) ** 4
        bass = 0.5 * bass_env * np.sin(2.0 * np.pi * bass_freq * t)

        # Mid-range tone: slowly sweeping between 300-600 Hz
        mid_freq = 450.0 + 150.0 * np.sin(2.0 * np.pi * 0.1 * t_now)
        mid = 0.25 * np.sin(2.0 * np.pi * mid_freq * t)

        # High shimmer: ~3 kHz with faster modulation
        hi_freq = 3000.0 + 500.0 * np.sin(2.0 * np.pi * 0.3 * t_now)
        hi_env = 0.5 + 0.5 * np.sin(2.0 * np.pi * 4.0 * t)
        hi = 0.1 * hi_env * np.sin(2.0 * np.pi * hi_freq * t)

        # Mix mono signal
        mono = (bass + mid + hi).astype(np.float32)

        # Interleave stereo (identical L/R)
        stereo = np.empty(n * self._channels, dtype=np.float32)
        stereo[0::2] = mono
        stereo[1::2] = mono

        return stereo


class DemoAudioEngine(AudioCaptureEngine):
    """Virtual audio engine for demo mode.

    Provides virtual audio devices and produces synthetic audio data
    so the full audio-reactive pipeline works without real audio hardware.
    """

    ENGINE_TYPE = "demo"
    ENGINE_PRIORITY = 1000  # Highest priority in demo mode

    @classmethod
    def is_available(cls) -> bool:
        return is_demo_mode()

    @classmethod
    def get_default_config(cls) -> Dict[str, Any]:
        return {
            "sample_rate": 44100,
            "chunk_size": 1024,
        }

    @classmethod
    def enumerate_devices(cls) -> List[AudioDeviceInfo]:
        devices = []
        for idx, (name, is_loopback, channels, samplerate) in enumerate(_VIRTUAL_DEVICES):
            devices.append(AudioDeviceInfo(
                index=idx,
                name=name,
                is_input=True,
                is_loopback=is_loopback,
                channels=channels,
                default_samplerate=samplerate,
            ))
        logger.debug(f"Demo audio engine: {len(devices)} virtual device(s)")
        return devices

    @classmethod
    def create_stream(
        cls,
        device_index: int,
        is_loopback: bool,
        config: Dict[str, Any],
    ) -> DemoAudioCaptureStream:
        if device_index < 0 or device_index >= len(_VIRTUAL_DEVICES):
            raise ValueError(
                f"Invalid demo audio device index {device_index}. "
                f"Available: 0-{len(_VIRTUAL_DEVICES) - 1}"
            )
        merged = {**cls.get_default_config(), **config}
        return DemoAudioCaptureStream(device_index, is_loopback, merged)
