# ClawTex — Governed Agent with Genetic Memory
# Built by DeVere Cooley / AN2B LLC · an2b.com
# BB4C — Breath Before Code

"""
Base channel interface — all channel adapters inherit from BaseChannel.

Channels are responsible for:
  - Receiving incoming messages and converting them to InboundMessage
  - Sending OutboundMessage back to the user
  - Lifecycle management (start / stop)
"""

from __future__ import annotations

import abc
import asyncio
import logging
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Coroutine

if TYPE_CHECKING:
    pass

logger = logging.getLogger(__name__)

MessageHandler = Callable[["InboundMessage"], Coroutine[Any, Any, str]]


@dataclass
class InboundMessage:
    """Normalised incoming message from any channel."""

    text: str
    user_id: str
    channel: str  # e.g. "telegram", "slack", "cli"
    thread_id: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass
class OutboundMessage:
    """Normalised outgoing message to any channel."""

    text: str
    user_id: str
    channel: str
    thread_id: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


class BaseChannel(abc.ABC):
    """Abstract base for all channel adapters."""

    name: str = "base"

    def __init__(self, handler: MessageHandler) -> None:
        self._handler = handler
        self._running = False

    @abc.abstractmethod
    async def start(self) -> None:
        """Start listening for messages."""
        ...

    @abc.abstractmethod
    async def stop(self) -> None:
        """Stop the channel gracefully."""
        ...

    @abc.abstractmethod
    async def send(self, message: OutboundMessage) -> None:
        """Deliver *message* to the user."""
        ...

    async def handle(self, message: InboundMessage) -> str:
        """Route an inbound message through the agent handler and return the reply."""
        logger.debug("[%s] → %r (user=%s)", self.name, message.text[:80], message.user_id)
        reply = await self._handler(message)
        logger.debug("[%s] ← %r", self.name, reply[:80] if reply else "")
        return reply
