"""WebSocket channel implementation with two-way communication."""

from __future__ import annotations

import logging
from typing import Any, Callable

from .types import ChannelSubscriptionOptions, MessageCallback

logger = logging.getLogger(__name__)


class ChannelImpl:
    """Implementation of a WebSocket channel with two-way communication."""

    def __init__(
        self,
        name: str,
        send_fn: Callable[[str, Any], None],
        options: ChannelSubscriptionOptions | None = None,
        on_unsubscribe: Callable[[], None] | None = None,
        on_subscribed: Callable[[], None] | None = None,
        on_error: Callable[[Exception], None] | None = None,
        on_message: MessageCallback | None = None,
    ) -> None:
        self._name = name
        self._send_fn = send_fn
        self._options = options or ChannelSubscriptionOptions()
        self._on_unsubscribe_callback = on_unsubscribe
        self._on_subscribed_callback = on_subscribed
        self._on_error_callback = on_error

        self._callbacks: set[MessageCallback] = set()
        self._subscribed = False

        # Register the on_message callback if provided
        if on_message:
            self._callbacks.add(on_message)

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

    @property
    def is_subscribed(self) -> bool:
        """Check if subscribed to this channel."""
        return self._subscribed

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

        Args:
            callback: Function to call when a message is received

        Returns:
            Unsubscribe function
        """
        self._callbacks.add(callback)

        def unsubscribe() -> None:
            self._callbacks.discard(callback)

        return unsubscribe

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

        Args:
            event: Event type
            data: Payload data
        """
        self._send_fn(event, data)

    def unsubscribe(self) -> None:
        """Unsubscribe from this channel."""
        self._callbacks.clear()
        self._subscribed = False
        if self._on_unsubscribe_callback:
            self._on_unsubscribe_callback()

    def mark_subscribed(self) -> None:
        """Mark channel as subscribed (called by connection manager).

        Internal method.
        """
        self._subscribed = True
        if self._on_subscribed_callback:
            self._on_subscribed_callback()

    def mark_unsubscribed(self) -> None:
        """Mark channel as unsubscribed (called by connection manager).

        Internal method.
        """
        self._subscribed = False

    def handle_message(self, event: str, data: Any) -> None:
        """Handle an incoming message (called by connection manager).

        Internal method.
        """
        for callback in self._callbacks:
            try:
                callback(event, data)
            except Exception as e:
                logger.error(f"Error in channel {self._name} callback: {e}")

    def handle_error(self, error: Exception) -> None:
        """Handle an error (called by connection manager).

        Internal method.
        """
        if self._on_error_callback:
            self._on_error_callback(error)

    def get_options(self) -> ChannelSubscriptionOptions:
        """Get subscription options.

        Internal method.
        """
        return self._options

    def has_listeners(self) -> bool:
        """Check if channel has any active listeners.

        Internal method.
        """
        return len(self._callbacks) > 0
