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

"""
Slack channel adapter — connects a ClawTex agent to a Slack workspace.

Requires: slack_bolt >= 1.x and SLACK_BOT_TOKEN + SLACK_APP_TOKEN env vars.
Uses Socket Mode for real-time message handling without a public HTTP endpoint.

Usage:
    channel = SlackChannel(handler=agent.handle_message)
    await channel.start()
"""

from __future__ import annotations

import asyncio
import logging
import os
from typing import Any

from .base import BaseChannel, InboundMessage, MessageHandler, OutboundMessage

logger = logging.getLogger(__name__)


class SlackChannel(BaseChannel):
    """Slack channel adapter using Slack Bolt (Socket Mode)."""

    name = "slack"

    def __init__(
        self,
        handler: MessageHandler,
        bot_token: str | None = None,
        app_token: str | None = None,
    ) -> None:
        super().__init__(handler)
        self._bot_token = bot_token or os.environ.get("SLACK_BOT_TOKEN", "")
        self._app_token = app_token or os.environ.get("SLACK_APP_TOKEN", "")
        self._app: Any = None
        self._handler_ref = handler

    async def start(self) -> None:
        if not self._bot_token:
            raise ValueError("SLACK_BOT_TOKEN is not set.")
        if not self._app_token:
            raise ValueError(
                "SLACK_APP_TOKEN is not set. "
                "Create an app-level token with connections:write scope."
            )

        try:
            from slack_bolt.async_app import AsyncApp
            from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
        except ImportError as exc:
            raise ImportError(
                "slack_bolt is not installed. Run: pip install slack-bolt"
            ) from exc

        self._app = AsyncApp(token=self._bot_token)

        @self._app.event("message")
        async def _on_message(event: dict[str, Any], say: Any) -> None:
            # Ignore bot messages and subtypes (edits, deletions)
            if event.get("bot_id") or event.get("subtype"):
                return
            text = event.get("text", "")
            if not text:
                return

            msg = InboundMessage(
                text=text,
                user_id=event.get("user", "unknown"),
                channel=self.name,
                thread_id=event.get("thread_ts") or event.get("ts"),
                metadata={
                    "channel_id": event.get("channel"),
                    "ts": event.get("ts"),
                    "thread_ts": event.get("thread_ts"),
                },
            )
            reply_text = await self.handle(msg)
            # Reply in thread if the message was in a thread
            thread_ts = event.get("thread_ts") or event.get("ts")
            await say(text=reply_text, thread_ts=thread_ts)

        self._running = True
        logger.info("Slack channel starting (Socket Mode)…")
        handler = AsyncSocketModeHandler(self._app, self._app_token)
        await handler.start_async()

    async def stop(self) -> None:
        self._running = False
        if self._app:
            logger.info("Slack channel stopped.")

    async def send(self, message: OutboundMessage) -> None:
        if not self._app:
            logger.warning("Slack send called before start()")
            return
        channel_id = message.metadata.get("channel_id") or message.user_id
        thread_ts = message.metadata.get("thread_ts")
        kwargs: dict[str, Any] = {"channel": channel_id, "text": message.text}
        if thread_ts:
            kwargs["thread_ts"] = thread_ts
        await self._app.client.chat_postMessage(**kwargs)
