"""Base channel interface for communication channels.

This module defines the abstract interface that all channel implementations
must follow, allowing the orchestrator to work with any communication platform.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional


class ChannelType(str, Enum):
    """Supported channel types."""

    SLACK = "slack"
    TEAMS = "teams"
    WHATSAPP = "whatsapp"
    API = "api"
    WEBHOOK = "webhook"
    CUSTOM = "custom"


@dataclass
class ChannelMessage:
    """Incoming message from a channel."""

    channel_type: ChannelType
    channel_id: str  # Channel/conversation ID
    user_id: str  # User/sender ID
    text: str  # Message text
    thread_id: Optional[str] = None  # Thread/reply context
    metadata: Dict[str, Any] = field(default_factory=dict)
    raw_event: Optional[Dict[str, Any]] = None

    @property
    def session_id(self) -> str:
        """Generate a unique session ID for this conversation."""
        if self.thread_id:
            return f"{self.channel_type.value}:{self.channel_id}:{self.thread_id}"
        return f"{self.channel_type.value}:{self.channel_id}:{self.user_id}"


@dataclass
class ChannelResponse:
    """Response to send to a channel."""

    text: str
    channel_id: str
    thread_id: Optional[str] = None
    blocks: Optional[List[Dict[str, Any]]] = None  # Rich formatting (Slack blocks, etc.)
    attachments: Optional[List[Dict[str, Any]]] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


class BaseChannel(ABC):
    """Abstract base class for communication channel implementations.

    All channel implementations (Slack, Teams, API, etc.) must inherit
    from this class and implement the required methods.

    Example:
        class MyCustomChannel(BaseChannel):
            def send_message(self, response):
                # Send message to your channel
                pass

            def parse_event(self, event):
                return ChannelMessage(...)

            @property
            def channel_type(self):
                return ChannelType.CUSTOM
    """

    @abstractmethod
    def send_message(self, response: ChannelResponse) -> bool:
        """Send a message to the channel.

        Args:
            response: The response to send

        Returns:
            True if sent successfully, False otherwise
        """
        pass

    @abstractmethod
    def parse_event(self, event: Dict[str, Any]) -> Optional[ChannelMessage]:
        """Parse an incoming event into a ChannelMessage.

        Args:
            event: Raw event from the channel

        Returns:
            Parsed ChannelMessage or None if not a valid message
        """
        pass

    @property
    @abstractmethod
    def channel_type(self) -> ChannelType:
        """Get the channel type.

        Returns:
            ChannelType enum value
        """
        pass

    def verify_request(self, headers: Dict[str, str], body: bytes) -> bool:
        """Verify the authenticity of an incoming request.

        Override this method to implement signature verification.

        Args:
            headers: Request headers
            body: Raw request body

        Returns:
            True if request is valid, False otherwise
        """
        return True

    def format_response(self, text: str, channel_id: str, **kwargs) -> ChannelResponse:
        """Create a formatted response for this channel.

        Override for channel-specific formatting.

        Args:
            text: Response text
            channel_id: Target channel ID
            **kwargs: Additional formatting options

        Returns:
            Formatted ChannelResponse
        """
        return ChannelResponse(
            text=text,
            channel_id=channel_id,
            thread_id=kwargs.get("thread_id"),
            metadata=kwargs,
        )

    def get_context(self, message: ChannelMessage) -> Dict[str, Any]:
        """Extract context to inject into tool calls.

        Override to provide channel-specific context.

        Args:
            message: Incoming message

        Returns:
            Context dictionary to inject into tools
        """
        return {
            "channel_type": self.channel_type.value,
            "channel_id": message.channel_id,
            "user_id": message.user_id,
        }


class ChannelError(Exception):
    """Base exception for channel errors."""

    def __init__(
        self,
        message: str,
        channel_type: Optional[ChannelType] = None,
        is_retryable: bool = False,
    ):
        super().__init__(message)
        self.channel_type = channel_type
        self.is_retryable = is_retryable


class ChannelAuthError(ChannelError):
    """Raised when channel authentication fails."""

    pass


class ChannelSendError(ChannelError):
    """Raised when sending a message fails."""

    pass


class ChannelAccessDeniedError(ChannelError):
    """Raised when access to a channel is denied.

    Used for channel allowlist/blocklist filtering.
    """

    def __init__(
        self,
        message: str,
        channel_id: str,
        channel_type: Optional[ChannelType] = None,
    ):
        super().__init__(message, channel_type=channel_type, is_retryable=False)
        self.channel_id = channel_id
