"""Domain models for DLNA servers, containers and media items."""

from __future__ import annotations

from collections.abc import Sequence
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Optional
from urllib.parse import urlparse


class ItemType(Enum):
    CONTAINER = "container"
    AUDIO = "audio"
    VIDEO = "video"
    IMAGE = "image"
    UNKNOWN = "unknown"


@dataclass
class MediaServer:
    """Represents a discovered DLNA MediaServer on the LAN."""
    friendly_name: str
    location: str  # description URL
    udn: str = ""
    icon_url: str = ""

    def __str__(self) -> str:
        return self.friendly_name or self.location


@dataclass
class MediaItem:
    """A single content-directory entry (container or leaf item)."""
    object_id: str
    title: str
    item_type: ItemType = ItemType.UNKNOWN
    parent_id: str = "0"
    resource_url: str = ""
    mime_type: str = ""
    size: int = 0
    duration: str = ""
    album_art_url: str = ""
    child_count: int = 0

    @property
    def is_container(self) -> bool:
        return self.item_type == ItemType.CONTAINER

    def __str__(self) -> str:
        return self.title or self.object_id


_SUBTITLE_EXTENSIONS = {".ass", ".idx", ".srt", ".ssa", ".sub", ".sup", ".ttml", ".vtt"}
_VIDEO_EXTENSIONS = {".avi", ".m2ts", ".m4v", ".mkv", ".mov", ".mp4", ".mpeg", ".mpg", ".ts", ".webm"}
_AUDIO_EXTENSIONS = {".aac", ".flac", ".m4a", ".mp3", ".ogg", ".opus", ".wav"}
_IMAGE_EXTENSIONS = {".bmp", ".gif", ".jpeg", ".jpg", ".png", ".webp"}
_SUBTITLE_MIME_MARKERS = (
    "application/ass",
    "application/pgs",
    "application/sdp",
    "application/smil",
    "application/srt",
    "application/ssa",
    "application/ttml+xml",
    "application/vtt",
    "application/x-subrip",
    "subpicture/",
    "subtitle/",
    "text/",
)


def _extract_protocol_mime(protocol_info: str) -> str:
    parts = protocol_info.split(":")
    if len(parts) >= 3:
        return parts[2]
    return protocol_info


def _resource_kind_from_mime(mime_type: str) -> ItemType | str | None:
    mime = mime_type.lower()
    if not mime:
        return None
    if any(marker in mime for marker in _SUBTITLE_MIME_MARKERS):
        return "text"
    if mime.startswith("video/"):
        return ItemType.VIDEO
    if mime.startswith("audio/"):
        return ItemType.AUDIO
    if mime.startswith("image/"):
        return ItemType.IMAGE
    return None


def _resource_kind_from_url(url: str) -> ItemType | str | None:
    path = urlparse(url).path.lower()
    dot = path.rfind(".")
    ext = path[dot:] if dot >= 0 else ""
    if ext in _SUBTITLE_EXTENSIONS:
        return "text"
    if ext in _VIDEO_EXTENSIONS:
        return ItemType.VIDEO
    if ext in _AUDIO_EXTENSIONS:
        return ItemType.AUDIO
    if ext in _IMAGE_EXTENSIONS:
        return ItemType.IMAGE
    return None


def _normalize_resource(resource: dict[str, Any] | str) -> dict[str, Any]:
    if isinstance(resource, str):
        return {
            "url": resource,
            "protocol_info": "",
            "mime_type": "",
            "size": 0,
            "duration": "",
        }

    protocol_info = str(resource.get("protocol_info", resource.get("@protocolInfo", "")))
    mime_type = str(resource.get("mime_type", "")) or _extract_protocol_mime(protocol_info)
    return {
        "url": str(resource.get("url", resource.get("uri", resource.get("#text", "")))),
        "protocol_info": protocol_info,
        "mime_type": mime_type,
        "size": int(resource.get("size", resource.get("@size", 0)) or 0),
        "duration": str(resource.get("duration", resource.get("@duration", ""))),
    }


def select_best_resource(resources: Sequence[dict[str, Any] | str], item_type: ItemType) -> dict[str, Any] | None:
    best: dict[str, Any] | None = None
    best_key: tuple[int, int, int] | None = None

    for resource in resources:
        normalized = _normalize_resource(resource)
        url = normalized["url"]
        if not url:
            continue

        resource_kind = _resource_kind_from_mime(normalized["mime_type"]) or _resource_kind_from_url(url)
        score = 0
        if item_type != ItemType.UNKNOWN and resource_kind == item_type:
            score += 10
        elif resource_kind == "text":
            score -= 20
        elif item_type != ItemType.UNKNOWN and resource_kind is not None:
            score -= 4
        elif resource_kind in {ItemType.AUDIO, ItemType.VIDEO, ItemType.IMAGE}:
            score += 3

        if normalized["protocol_info"].startswith("http-get:"):
            score += 1

        key = (score, int(normalized["size"]), len(url))
        if best_key is None or key > best_key:
            best = normalized
            best_key = key

    return best


def classify_upnp_class(upnp_class: str) -> ItemType:
    """Map a UPnP class string to our ItemType enum."""
    c = upnp_class.lower()
    if "container" in c:
        return ItemType.CONTAINER
    if "audio" in c:
        return ItemType.AUDIO
    if "video" in c:
        return ItemType.VIDEO
    if "image" in c:
        return ItemType.IMAGE
    return ItemType.UNKNOWN


def parse_didl_item(didl_item: dict) -> MediaItem:
    """Build a MediaItem from a parsed DIDL-Lite dict (as returned by async-upnp-client)."""
    title = didl_item.get("title", "")
    object_id = didl_item.get("id", didl_item.get("@id", ""))
    parent_id = didl_item.get("parent_id", didl_item.get("@parentID", "0"))
    upnp_class = didl_item.get("upnp_class", didl_item.get("class", ""))
    item_type = classify_upnp_class(upnp_class)
    child_count = int(didl_item.get("child_count", didl_item.get("@childCount", 0)) or 0)

    resource_url = ""
    mime_type = ""
    size = 0
    duration = ""
    resources = didl_item.get("resources", didl_item.get("res", []))
    if isinstance(resources, dict):
        resources = [resources]
    selected_resource = select_best_resource(resources, item_type)
    if selected_resource is not None:
        resource_url = str(selected_resource["url"])
        mime_type = str(selected_resource["protocol_info"] or selected_resource["mime_type"])
        size = int(selected_resource["size"])
        duration = str(selected_resource["duration"])

    album_art = didl_item.get("album_art_uri", didl_item.get("albumArtURI", ""))

    return MediaItem(
        object_id=str(object_id),
        title=str(title),
        item_type=item_type,
        parent_id=str(parent_id),
        resource_url=str(resource_url),
        mime_type=str(mime_type),
        size=size,
        duration=str(duration),
        album_art_url=str(album_art),
        child_count=child_count,
    )
