"""WebSocket channel types.

Wire protocol:
- Client → Server: Uses "action" field (subscribe, unsubscribe, message, ping)
- Server → Client: Uses "event" field (subscribed, data, error, pong)
"""

from enum import Enum
from typing import Any, Callable, Protocol

from pydantic import BaseModel, Field


class ConnectionState(str, Enum):
    """Connection state enum."""

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


# ============================================================================
# Client → Server Messages (action-based)
# ============================================================================


class SubscribeAction(BaseModel):
    """Subscribe action - client wants to subscribe to a channel."""

    action: str = Field(default="subscribe")
    channel: str = Field(..., description="Channel name")
    payload: dict[str, Any] | None = Field(default=None, description="Optional parameters")


class UnsubscribeAction(BaseModel):
    """Unsubscribe action - client wants to unsubscribe from a channel."""

    action: str = Field(default="unsubscribe")
    channel: str = Field(..., description="Channel name")


class MessageAction(BaseModel):
    """Message action - client sends a message to a channel."""

    action: str = Field(default="message")
    channel: str = Field(..., description="Channel name")
    event: str = Field(..., description="Event type")
    payload: Any = Field(..., description="Message payload")


class PingAction(BaseModel):
    """Ping action - client heartbeat."""

    action: str = Field(default="ping")


# ============================================================================
# Server → Client Messages (event-based)
# ============================================================================


class ServerChannelMessage(BaseModel):
    """Server message with channel context."""

    channel: str = Field(..., description="Channel name")
    event: str = Field(..., description="Event type")
    data: Any = Field(default=None, description="Message data")


class ServerErrorData(BaseModel):
    """Server error data."""

    code: str = Field(..., description="Error code")
    message: str = Field(..., description="Error message")


class ServerErrorMessage(BaseModel):
    """Server error message (may have channel context)."""

    channel: str | None = Field(default=None, description="Channel name (optional)")
    event: str = Field(default="error")
    data: ServerErrorData = Field(..., description="Error details")


class ServerPongMessage(BaseModel):
    """Server pong message (no channel context)."""

    event: str = Field(default="pong")


# ============================================================================
# Callbacks and Configuration Types
# ============================================================================


# Type alias for message callback
MessageCallback = Callable[[str, Any], None]

# Type alias for connection event listener
ConnectionEventListener = Callable[[ConnectionState], None]


class ChannelSubscriptionOptions(BaseModel):
    """Channel subscription options."""

    params: dict[str, str] | None = Field(default=None, description="Subscribe parameters")

    class Config:
        arbitrary_types_allowed = True


class Channel(Protocol):
    """Channel interface with two-way communication."""

    @property
    def name(self) -> str:
        """Channel name."""
        ...

    @property
    def is_subscribed(self) -> bool:
        """Check if subscribed."""
        ...

    def subscribe(self, callback: MessageCallback) -> Callable[[], None]:
        """Subscribe to messages on this channel.

        Returns unsubscribe function.
        """
        ...

    def send(self, event: str, data: Any) -> None:
        """Send a message to this channel."""
        ...

    def unsubscribe(self) -> None:
        """Unsubscribe from this channel."""
        ...


class WebSocketClientConfig(BaseModel):
    """WebSocket client configuration."""

    url: str = Field(..., description="WebSocket server URL")
    auto_reconnect: bool = Field(default=True, description="Whether to automatically reconnect")
    heartbeat_interval: float = Field(default=30.0, description="Heartbeat interval in seconds")

    class Config:
        arbitrary_types_allowed = True


class ReconnectConfig(BaseModel):
    """Reconnection configuration."""

    initial_delay: float = Field(default=1.0, description="Initial delay in seconds")
    max_delay: float = Field(default=30.0, description="Maximum delay in seconds")
    multiplier: float = Field(default=2.0, description="Multiplier for exponential backoff")
    max_attempts: int | None = Field(default=None, description="Maximum attempts (None = infinite)")
