"""Audio source data model with inheritance-based source types.

An AudioSource represents a reusable audio input configuration:
  CaptureAudioSource      — wraps a physical audio device (index + loopback flag)
  ProcessedAudioSource    — applies audio processing filters to another audio source
"""

from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Dict, List, Optional, Type


@dataclass
class AudioSource:
    """Base class for audio source configurations."""

    id: str
    name: str
    source_type: str  # "capture" | "processed"
    created_at: datetime
    updated_at: datetime
    description: Optional[str] = None
    tags: List[str] = field(default_factory=list)

    def to_dict(self) -> dict:
        """Convert source to dictionary. Subclasses extend this."""
        return {
            "id": self.id,
            "name": self.name,
            "source_type": self.source_type,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
            "description": self.description,
            "tags": self.tags,
            # Subclass fields default to None for forward compat
            "device_index": None,
            "is_loopback": None,
            "audio_template_id": None,
            "audio_source_id": None,
            "audio_processing_template_id": None,
        }

    @staticmethod
    def from_dict(data: dict) -> "AudioSource":
        """Factory: dispatch to the correct subclass based on source_type."""
        source_type = data.get("source_type", "capture") or "capture"
        subcls = _AUDIO_SOURCE_MAP.get(source_type)
        if subcls is None:
            # Fall back to capture for unknown types
            subcls = CaptureAudioSource
        return subcls.from_dict(data)


def _parse_common_fields(data: dict) -> dict:
    """Extract common fields shared by all audio source types."""
    raw_created = data.get("created_at")
    created_at = (
        datetime.fromisoformat(raw_created)
        if isinstance(raw_created, str)
        else raw_created if isinstance(raw_created, datetime) else datetime.now(timezone.utc)
    )
    raw_updated = data.get("updated_at")
    updated_at = (
        datetime.fromisoformat(raw_updated)
        if isinstance(raw_updated, str)
        else raw_updated if isinstance(raw_updated, datetime) else datetime.now(timezone.utc)
    )
    return dict(
        id=data["id"],
        name=data["name"],
        description=data.get("description"),
        tags=data.get("tags", []),
        created_at=created_at,
        updated_at=updated_at,
    )


@dataclass
class CaptureAudioSource(AudioSource):
    """Audio source wrapping a physical audio device.

    Captures all channels from the device.  For WASAPI loopback devices
    (system audio output), set is_loopback=True.
    """

    device_index: int = -1  # -1 = default device
    is_loopback: bool = True  # True = WASAPI loopback (system audio)
    audio_template_id: Optional[str] = None  # references AudioCaptureTemplate

    def to_dict(self) -> dict:
        d = super().to_dict()
        d["device_index"] = self.device_index
        d["is_loopback"] = self.is_loopback
        d["audio_template_id"] = self.audio_template_id
        return d

    @classmethod
    def from_dict(cls, data: dict) -> "CaptureAudioSource":
        common = _parse_common_fields(data)
        return cls(
            **common,
            source_type="capture",
            device_index=int(data.get("device_index", -1)),
            is_loopback=bool(data.get("is_loopback", True)),
            audio_template_id=data.get("audio_template_id"),
        )


@dataclass
class ProcessedAudioSource(AudioSource):
    """Audio source that applies processing filters to another audio source.

    References an existing audio source (capture or processed) and an
    AudioProcessingTemplate containing the filter chain to apply.
    """

    audio_source_id: str = ""  # references any AudioSource
    audio_processing_template_id: str = ""  # references AudioProcessingTemplate

    def to_dict(self) -> dict:
        d = super().to_dict()
        d["audio_source_id"] = self.audio_source_id
        d["audio_processing_template_id"] = self.audio_processing_template_id
        return d

    @classmethod
    def from_dict(cls, data: dict) -> "ProcessedAudioSource":
        common = _parse_common_fields(data)
        return cls(
            **common,
            source_type="processed",
            audio_source_id=data.get("audio_source_id") or "",
            audio_processing_template_id=data.get("audio_processing_template_id") or "",
        )


# -- Source type registry --
_AUDIO_SOURCE_MAP: Dict[str, Type[AudioSource]] = {
    "capture": CaptureAudioSource,
    "processed": ProcessedAudioSource,
}
