"""Typed CRP v0.1 message contracts."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Literal

from .constants import CRP_VERSION
from .errors import ProtocolValidationError


def _require_non_empty(value: str, field_name: str) -> None:
    if not isinstance(value, str) or not value.strip():
        raise ProtocolValidationError(f"'{field_name}' must be a non-empty string")


@dataclass(slots=True)
class ClipboardFormat:
    mime: str
    encoding: str
    data: str

    def __post_init__(self) -> None:
        _require_non_empty(self.mime, "mime")
        _require_non_empty(self.encoding, "encoding")
        _require_non_empty(self.data, "data")


@dataclass(slots=True)
class JoinMessage:
    session: str
    auth: str
    client_name: str
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["join"] = field(default="join", init=False)

    def __post_init__(self) -> None:
        _require_non_empty(self.session, "session")
        _require_non_empty(self.auth, "auth")
        _require_non_empty(self.client_name, "client_name")


@dataclass(slots=True)
class JoinAckMessage:
    session: str
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["join_ack"] = field(default="join_ack", init=False)

    def __post_init__(self) -> None:
        _require_non_empty(self.session, "session")


@dataclass(slots=True)
class ClipMessage:
    session: str
    message_id: str
    formats: list[ClipboardFormat]
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["clip"] = field(default="clip", init=False)

    def __post_init__(self) -> None:
        _require_non_empty(self.session, "session")
        _require_non_empty(self.message_id, "message_id")
        if not isinstance(self.formats, list) or not self.formats:
            raise ProtocolValidationError("'formats' must be a non-empty list")


@dataclass(slots=True)
class LeaveMessage:
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["leave"] = field(default="leave", init=False)


@dataclass(slots=True)
class HeartbeatMessage:
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["heartbeat"] = field(default="heartbeat", init=False)


@dataclass(slots=True)
class ErrorMessage:
    message: str
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["error"] = field(default="error", init=False)

    def __post_init__(self) -> None:
        _require_non_empty(self.message, "message")


@dataclass(slots=True)
class DiscoverMessage:
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["discover"] = field(default="discover", init=False)


@dataclass(slots=True)
class DiscoverReplyMessage:
    session: str
    requires_passphrase: bool
    host: str
    port: int
    protocol: str = field(default=CRP_VERSION, init=False)
    type: Literal["discover_reply"] = field(default="discover_reply", init=False)

    def __post_init__(self) -> None:
        _require_non_empty(self.session, "session")
        _require_non_empty(self.host, "host")
        if not isinstance(self.requires_passphrase, bool):
            raise ProtocolValidationError("'requires_passphrase' must be boolean")
        if not isinstance(self.port, int) or self.port <= 0:
            raise ProtocolValidationError("'port' must be a positive integer")

