"""async_upnp_client.const module."""

from dataclasses import dataclass
from datetime import date, datetime, time
from enum import Enum
from ipaddress import IPv4Address, IPv6Address
from typing import Any, Callable, Mapping, MutableMapping, NamedTuple
from xml.etree import ElementTree as ET

from async_upnp_client.utils import parse_date_time, require_tzinfo

IPvXAddress = IPv4Address | IPv6Address  # pylint: disable=invalid-name
AddressTupleV4Type = tuple[str, int]
AddressTupleV6Type = tuple[str, int, int, int]
AddressTupleVXType = AddressTupleV4Type | AddressTupleV6Type  # pylint: disable=invalid-name

NS = {
    "soap_envelope": "http://schemas.xmlsoap.org/soap/envelope/",
    "device": "urn:schemas-upnp-org:device-1-0",
    "service": "urn:schemas-upnp-org:service-1-0",
    "event": "urn:schemas-upnp-org:event-1-0",
    "control": "urn:schemas-upnp-org:control-1-0",
}


MIME_TO_UPNP_CLASS_MAPPING: Mapping[str, str] = {
    "audio": "object.item.audioItem",
    "video": "object.item.videoItem",
    "image": "object.item.imageItem",
    "application/dash+xml": "object.item.videoItem",
    "application/x-mpegurl": "object.item.videoItem",
    "application/vnd.apple.mpegurl": "object.item.videoItem",
}


STATE_VARIABLE_TYPE_MAPPING: Mapping[str, Mapping[str, Callable]] = {
    "ui1": {"type": int, "in": int, "out": str},
    "ui2": {"type": int, "in": int, "out": str},
    "ui4": {"type": int, "in": int, "out": str},
    "ui8": {"type": int, "in": int, "out": str},
    "i1": {"type": int, "in": int, "out": str},
    "i2": {"type": int, "in": int, "out": str},
    "i4": {"type": int, "in": int, "out": str},
    "i8": {"type": int, "in": int, "out": str},
    "int": {"type": int, "in": int, "out": str},
    "r4": {"type": float, "in": float, "out": str},
    "r8": {"type": float, "in": float, "out": str},
    "number": {"type": float, "in": float, "out": str},
    "fixed.14.4": {"type": float, "in": float, "out": str},
    "float": {"type": float, "in": float, "out": str},
    "char": {"type": str, "in": str, "out": str},
    "string": {"type": str, "in": str, "out": str},
    "boolean": {
        "type": bool,
        "in": lambda s: s.lower() in ["1", "true", "yes"],
        "out": lambda b: "1" if b else "0",
    },
    "bin.base64": {"type": str, "in": str, "out": str},
    "bin.hex": {"type": str, "in": str, "out": str},
    "uri": {"type": str, "in": str, "out": str},
    "uuid": {"type": str, "in": str, "out": str},
    "date": {"type": date, "in": parse_date_time, "out": lambda d: d.isoformat()},
    "dateTime": {
        "type": datetime,
        "in": parse_date_time,
        "out": lambda dt: dt.isoformat("T", "seconds"),
    },
    "dateTime.tz": {
        "type": datetime,
        "validator": require_tzinfo,
        "in": parse_date_time,
        "out": lambda dt: dt.isoformat("T", "seconds"),
    },
    "time": {
        "type": time,
        "in": parse_date_time,
        "out": lambda t: t.isoformat("seconds"),
    },
    "time.tz": {
        "type": time,
        "validator": require_tzinfo,
        "in": parse_date_time,
        "out": lambda t: t.isoformat("T", "seconds"),
    },
}


class DeviceIcon(NamedTuple):
    """Device icon."""

    mimetype: str
    width: int
    height: int
    depth: int
    url: str


class DeviceInfo(NamedTuple):
    """Device info."""

    device_type: str
    friendly_name: str
    manufacturer: str
    manufacturer_url: str | None
    model_description: str | None
    model_name: str
    model_number: str | None
    model_url: str | None
    serial_number: str | None
    udn: str
    upc: str | None
    presentation_url: str | None
    url: str
    icons: list[DeviceIcon]
    xml: ET.Element


class ServiceInfo(NamedTuple):
    """Service info."""

    service_id: str
    service_type: str
    control_url: str
    event_sub_url: str
    scpd_url: str
    xml: ET.Element


class ActionArgumentInfo(NamedTuple):
    """Action argument info."""

    name: str
    direction: str
    state_variable_name: str
    xml: ET.Element


class ActionInfo(NamedTuple):
    """Action info."""

    name: str
    arguments: list[ActionArgumentInfo]
    xml: ET.Element


@dataclass(frozen=True)
class HttpRequest:
    """HTTP request."""

    method: str
    url: str
    headers: Mapping[str, str]
    body: str | None


@dataclass(frozen=True)
class HttpResponse:
    """HTTP response."""

    status_code: int
    headers: Mapping[str, str]
    body: str | None


@dataclass(frozen=True)
class StateVariableTypeInfo:
    """State variable type info."""

    data_type: str
    data_type_mapping: Mapping[str, Callable]
    default_value: str | None
    allowed_value_range: Mapping[str, str | None]
    allowed_values: list[str] | None
    xml: ET.Element


@dataclass(frozen=True)
class EventableStateVariableTypeInfo(StateVariableTypeInfo):
    """Eventable State variable type info."""

    max_rate: float | None


@dataclass(frozen=True)
class StateVariableInfo:
    """State variable info."""

    name: str
    send_events: bool
    type_info: StateVariableTypeInfo
    xml: ET.Element


# Headers
SsdpHeaders = MutableMapping[str, Any]
NotificationType = str  # NT header
UniqueServiceName = str  # USN header
SearchTarget = str  # ST header
UniqueDeviceName = str  # UDN
DeviceOrServiceType = str


# Event handler
ServiceId = str  # SID


class NotificationSubType(str, Enum):
    """NTS header."""

    SSDP_ALIVE = "ssdp:alive"
    SSDP_BYEBYE = "ssdp:byebye"
    SSDP_UPDATE = "ssdp:update"


class SsdpSource(str, Enum):
    """SSDP source."""

    ADVERTISEMENT = "advertisement"
    SEARCH = "search"

    # More detailed
    SEARCH_ALIVE = "search_alive"
    SEARCH_CHANGED = "search_changed"

    # More detailed.
    ADVERTISEMENT_ALIVE = "advertisement_alive"
    ADVERTISEMENT_BYEBYE = "advertisement_byebye"
    ADVERTISEMENT_UPDATE = "advertisement_update"
