"""Slack integration via Slack Web API."""

import os

import httpx

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


def _headers() -> dict:
    token = os.environ.get("SLACK_BOT_TOKEN")
    if not token:
        raise ValueError(
            "SLACK_BOT_TOKEN not set. Create a Slack app at https://api.slack.com/apps, "
            "add bot scopes (chat:write, channels:read, channels:history, reactions:write, pins:write), "
            "install to workspace, and copy the Bot User OAuth Token."
        )
    return {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
    }


async def _resolve_channel(client: httpx.AsyncClient, headers: dict, channel: str) -> str:
    """Resolve channel name to ID if needed. Passthrough if already an ID."""
    if channel.startswith("C") or channel.startswith("G"):
        return channel

    # Strip leading # if present
    channel = channel.lstrip("#")

    resp = await client.get(
        f"{SLACK_API}/conversations.list",
        headers=headers,
        params={"types": "public_channel,private_channel", "limit": 200},
    )
    resp.raise_for_status()
    data = resp.json()

    if not data.get("ok"):
        raise ValueError(f"Slack API error: {data.get('error', 'unknown')}")

    for ch in data.get("channels", []):
        if ch.get("name") == channel:
            return ch["id"]

    raise ValueError(f"Channel '{channel}' not found. Make sure the bot is invited to the channel.")


async def _slack_post(client: httpx.AsyncClient, headers: dict, method: str, body: dict) -> dict:
    """POST to Slack API and check response."""
    resp = await client.post(f"{SLACK_API}/{method}", headers=headers, json=body)
    resp.raise_for_status()
    data = resp.json()

    if not data.get("ok"):
        error = data.get("error", "unknown_error")
        raise ValueError(f"Slack API error: {error}")

    return data


async def handler(params: dict) -> dict:
    """Handle Slack operations."""
    action = params["action"]
    headers = _headers()

    try:
        async with httpx.AsyncClient(timeout=30) as client:
            if action == "send_message":
                return await _send_message(client, headers, params)
            elif action == "react":
                return await _react(client, headers, params)
            elif action == "pin":
                return await _pin(client, headers, params)
            elif action == "list_channels":
                return await _list_channels(client, headers)
            elif action == "read_messages":
                return await _read_messages(client, headers, params)
            else:
                return {"success": False, "message": f"Unknown action: {action}", "data": {}}
    except ValueError:
        raise
    except httpx.HTTPStatusError as e:
        return {"success": False, "message": f"Slack HTTP error: {e.response.status_code}", "data": {}}
    except Exception as e:
        return {"success": False, "message": f"Error: {e}", "data": {}}


async def _send_message(client: httpx.AsyncClient, headers: dict, params: dict) -> dict:
    channel = params.get("channel")
    text = params.get("text")
    if not channel or not text:
        raise ValueError("'channel' and 'text' are required for send_message")

    channel_id = await _resolve_channel(client, headers, channel)
    body = {"channel": channel_id, "text": text}
    if params.get("thread_ts"):
        body["thread_ts"] = params["thread_ts"]

    data = await _slack_post(client, headers, "chat.postMessage", body)
    return {
        "success": True,
        "message": f"Message sent to #{channel}",
        "data": {"ts": data.get("ts"), "channel": data.get("channel")},
    }


async def _react(client: httpx.AsyncClient, headers: dict, params: dict) -> dict:
    channel = params.get("channel")
    emoji = params.get("emoji")
    message_ts = params.get("message_ts")
    if not channel or not emoji or not message_ts:
        raise ValueError("'channel', 'emoji', and 'message_ts' are required for react")

    channel_id = await _resolve_channel(client, headers, channel)
    await _slack_post(client, headers, "reactions.add", {
        "channel": channel_id,
        "name": emoji,
        "timestamp": message_ts,
    })
    return {"success": True, "message": f"Added :{emoji}: reaction", "data": {}}


async def _pin(client: httpx.AsyncClient, headers: dict, params: dict) -> dict:
    channel = params.get("channel")
    message_ts = params.get("message_ts")
    if not channel or not message_ts:
        raise ValueError("'channel' and 'message_ts' are required for pin")

    channel_id = await _resolve_channel(client, headers, channel)
    await _slack_post(client, headers, "pins.add", {
        "channel": channel_id,
        "timestamp": message_ts,
    })
    return {"success": True, "message": "Message pinned", "data": {}}


async def _list_channels(client: httpx.AsyncClient, headers: dict) -> dict:
    resp = await client.get(
        f"{SLACK_API}/conversations.list",
        headers=headers,
        params={"types": "public_channel,private_channel", "limit": 200},
    )
    resp.raise_for_status()
    data = resp.json()

    if not data.get("ok"):
        raise ValueError(f"Slack API error: {data.get('error', 'unknown')}")

    channels = [
        {
            "id": ch["id"],
            "name": ch.get("name", ""),
            "is_private": ch.get("is_private", False),
            "num_members": ch.get("num_members", 0),
            "topic": ch.get("topic", {}).get("value", ""),
        }
        for ch in data.get("channels", [])
    ]
    return {
        "success": True,
        "message": f"Found {len(channels)} channel(s)",
        "data": {"channels": channels},
    }


async def _read_messages(client: httpx.AsyncClient, headers: dict, params: dict) -> dict:
    channel = params.get("channel")
    if not channel:
        raise ValueError("'channel' is required for read_messages")

    channel_id = await _resolve_channel(client, headers, channel)
    limit = min(int(params.get("limit") or 10), 100)

    resp = await client.get(
        f"{SLACK_API}/conversations.history",
        headers=headers,
        params={"channel": channel_id, "limit": limit},
    )
    resp.raise_for_status()
    data = resp.json()

    if not data.get("ok"):
        raise ValueError(f"Slack API error: {data.get('error', 'unknown')}")

    messages = [
        {
            "ts": msg.get("ts"),
            "user": msg.get("user", ""),
            "text": f"<external_message>{msg.get('text', '')}</external_message>",
            "type": msg.get("subtype", "message"),
        }
        for msg in data.get("messages", [])
    ]
    return {
        "success": True,
        "message": f"Retrieved {len(messages)} message(s)",
        "data": {"messages": messages},
    }
