"""Connection metadata and URL parsing for Cthulhu Remote."""

from dataclasses import dataclass
from enum import Enum
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse

from .protocol import SERVER_PORT, URL_PREFIX
from .socket_utils import host_port_to_address


class URLParsingError(Exception):
    """Raised when a remote connection URL is incomplete or invalid."""


class ConnectionMode(Enum):
    """Remote session role."""

    MASTER = "master"
    SLAVE = "slave"


class ConnectionState(Enum):
    """Remote session connection state."""

    CONNECTED = "connected"
    CONNECTING = "connecting"
    DISCONNECTED = "disconnected"
    DISCONNECTING = "disconnecting"


@dataclass
class ConnectionInfo:
    """Relay connection settings."""

    hostname: str
    mode: ConnectionMode
    key: str
    port: int = SERVER_PORT
    insecure: bool = False

    def __post_init__(self) -> None:
        self.port = self.port or SERVER_PORT
        self.mode = ConnectionMode(self.mode)

    @classmethod
    def from_url(cls, url: str) -> "ConnectionInfo":
        parsedUrl = urlparse(url)
        parsedQuery = parse_qs(parsedUrl.query)
        hostname = parsedUrl.hostname
        port = parsedUrl.port or SERVER_PORT
        key = parsedQuery.get("key", [""])[0]
        mode = parsedQuery.get("mode", [""])[0].lower()
        insecure = parsedQuery.get("insecure", ["false"])[0].lower() == "true"
        if not hostname:
            raise URLParsingError("No hostname provided")
        if not key:
            raise URLParsingError("No key provided")
        if not mode:
            raise URLParsingError("No mode provided")
        try:
            ConnectionMode(mode)
        except ValueError as error:
            raise URLParsingError(f"Invalid mode provided: {mode!r}") from error
        return cls(hostname=hostname, mode=ConnectionMode(mode), key=key, port=port, insecure=insecure)

    def get_address(self) -> str:
        return host_port_to_address((self.hostname, self.port))

    def get_url(self, mode: ConnectionMode | None = None) -> str:
        mode = mode or self.mode
        query = {"key": self.key, "mode": mode.value}
        if self.insecure:
            query["insecure"] = "true"
        return urlunparse((URL_PREFIX.split("://", 1)[0], self.get_address(), "", "", urlencode(query), ""))

    def get_url_to_connect(self) -> str:
        mode = ConnectionMode.SLAVE if self.mode == ConnectionMode.MASTER else ConnectionMode.MASTER
        return self.get_url(mode)
