"""Slack connector — post messages and list channels via the Slack Web API."""

from __future__ import annotations

import httpx

from legate.agent.tooling import ToolContext, ToolResult, ToolSpec
from legate.connectors.base import BaseConnector, ConnectionResult
from legate.connectors.runtime import new_async_client

_API_BASE = "https://slack.com/api"


class SlackConnector(BaseConnector):
    type = "slack"
    title = "Slack"
    config_schema = {
        "type": "object",
        "properties": {"default_channel": {"type": "string"}},
    }
    secret_schema = {
        "type": "object",
        "properties": {"bot_token": {"type": "string"}},
        "required": ["bot_token"],
    }

    def list_tools(self) -> list[ToolSpec]:
        return [
            ToolSpec(
                name="slack.list_channels",
                description="List the channels the Slack bot can see.",
                parameters={"type": "object", "properties": {}},
                is_sensitive=False,
                scopes=["slack:read"],
            ),
            ToolSpec(
                name="slack.post_message",
                description="Post a message to a Slack channel.",
                parameters={
                    "type": "object",
                    "properties": {
                        "channel": {"type": "string", "description": "Channel id or name."},
                        "text": {"type": "string", "description": "Message text."},
                    },
                    "required": ["text"],
                },
                is_sensitive=False,
                scopes=["slack:write"],
            ),
            ToolSpec(
                name="slack.reply_thread",
                description="Reply to a Slack message thread.",
                parameters={
                    "type": "object",
                    "properties": {
                        "channel": {"type": "string"},
                        "thread_ts": {"type": "string"},
                        "text": {"type": "string"},
                    },
                    "required": ["channel", "thread_ts", "text"],
                },
                is_sensitive=False,
                scopes=["slack:write"],
            ),
        ]

    async def _post(self, method: str, token: str, payload: dict) -> dict:
        headers = {"Authorization": f"Bearer {token}"}
        async with new_async_client() as client:
            resp = await client.post(f"{_API_BASE}/{method}", json=payload, headers=headers)
            resp.raise_for_status()
            return resp.json()

    async def _get(self, method: str, token: str, params: dict) -> dict:
        headers = {"Authorization": f"Bearer {token}"}
        async with new_async_client() as client:
            resp = await client.get(f"{_API_BASE}/{method}", params=params, headers=headers)
            resp.raise_for_status()
            return resp.json()

    async def test_connection(self, config: dict, secrets: dict) -> ConnectionResult:
        try:
            data = await self._post("auth.test", secrets["bot_token"], {})
        except (httpx.HTTPError, KeyError) as exc:
            return ConnectionResult(ok=False, message=f"connection failed: {exc}")
        if data.get("ok"):
            return ConnectionResult(ok=True, message=f"connected as {data.get('user', 'bot')}")
        return ConnectionResult(ok=False, message=data.get("error", "auth.test failed"))

    async def execute(
        self, tool: str, args: dict, config: dict, secrets: dict, ctx: ToolContext
    ) -> ToolResult:
        token = secrets.get("bot_token")
        if not token:
            return ToolResult(ok=False, error="missing bot_token")
        try:
            if tool == "slack.list_channels":
                data = await self._get("conversations.list", token, {"limit": 200})
                if not data.get("ok"):
                    return ToolResult(ok=False, error=data.get("error", "slack error"))
                channels = [
                    {"id": c.get("id"), "name": c.get("name")} for c in data.get("channels", [])
                ]
                return ToolResult(ok=True, data={"channels": channels})

            if tool in {"slack.post_message", "slack.reply_thread"}:
                channel = args.get("channel") or config.get("default_channel")
                if not channel:
                    return ToolResult(ok=False, error="no channel specified")
                payload = {"channel": channel, "text": args.get("text", "")}
                if tool == "slack.reply_thread":
                    payload["thread_ts"] = args.get("thread_ts", "")
                data = await self._post("chat.postMessage", token, payload)
                if not data.get("ok"):
                    return ToolResult(ok=False, error=data.get("error", "slack error"))
                return ToolResult(
                    ok=True, data={"ts": data.get("ts"), "channel": data.get("channel")}
                )

            return ToolResult(ok=False, error=f"unknown slack tool: {tool}")
        except httpx.HTTPError as exc:
            return ToolResult(ok=False, error=f"slack request failed: {exc}")
