import asyncio
import json
import traceback
from quart import Quart, jsonify, request
from slack_sdk.web.async_client import AsyncWebClient
from .slackevent import SlackEvent
from typing import Callable
import langbot_plugin.api.entities.builtin.platform.events as platform_events

_MAX_CALLBACK_BODY_BYTES = 1024 * 1024


class SlackClient:
    def __init__(self, bot_token: str, signing_secret: str, logger: None, unified_mode: bool = False):
        self.bot_token = bot_token
        self.signing_secret = signing_secret
        self.unified_mode = unified_mode
        self.app = Quart(__name__)
        self.app.config['MAX_CONTENT_LENGTH'] = _MAX_CALLBACK_BODY_BYTES
        self.client = AsyncWebClient(self.bot_token)

        # 只有在非统一模式下才注册独立路由
        if not self.unified_mode:
            self.app.add_url_rule(
                '/callback/command', 'handle_callback', self.handle_callback_request, methods=['GET', 'POST']
            )

        self._message_handlers = {
            'example': [],
        }
        self.bot_user_id = None  # 避免机器人回复自己的消息
        self.logger = logger

    async def handle_callback_request(self):
        """处理回调请求（独立端口模式，使用全局 request）"""
        return await self._handle_callback_internal(request)

    async def handle_unified_webhook(self, req):
        """处理回调请求（统一 webhook 模式，显式传递 request）。

        Args:
            req: Quart Request 对象

        Returns:
            响应数据
        """
        return await self._handle_callback_internal(req)

    async def _handle_callback_internal(self, req):
        """处理回调请求的内部实现。

        Args:
            req: Quart Request 对象
        """
        try:
            body = await req.get_data()
            if len(body) > _MAX_CALLBACK_BODY_BYTES:
                raise ValueError('Slack callback body exceeds the size limit')
            data = await asyncio.to_thread(json.loads, body)
            if 'type' in data:
                if data['type'] == 'url_verification':
                    return data['challenge']

            bot_user_id = data.get('event', {}).get('bot_id', '')

            if self.bot_user_id and bot_user_id == self.bot_user_id:
                return jsonify({'status': 'ok'})

            # 处理私信
            if data and data.get('event', {}).get('channel_type') in ['im']:
                event = SlackEvent.from_payload(data)
                await self._handle_message(event)
                return jsonify({'status': 'ok'})

            # 处理群聊
            if data.get('event', {}).get('type') == 'app_mention':
                data.setdefault('event', {})['channel_type'] = 'channel'
                event = SlackEvent.from_payload(data)
                await self._handle_message(event)
                return jsonify({'status': 'ok'})

            return jsonify({'status': 'ok'})

        except Exception as e:
            await self.logger.error(f'Error in handle_callback_request: {traceback.format_exc()}')
            raise (e)

    async def _handle_message(self, event: SlackEvent):
        """
        处理消息事件。
        """
        msg_type = event.type
        if msg_type in self._message_handlers:
            for handler in self._message_handlers[msg_type]:
                await handler(event)

    def on_message(self, msg_type: str):
        """注册消息类型处理器"""

        def decorator(func: Callable[[platform_events.Event], None]):
            if msg_type not in self._message_handlers:
                self._message_handlers[msg_type] = []
            self._message_handlers[msg_type].append(func)
            return func

        return decorator

    async def send_message_to_channel(self, text: str, channel_id: str):
        try:
            response = await self.client.chat_postMessage(channel=channel_id, text=text)
            if self.bot_user_id is None and response.get('ok'):
                self.bot_user_id = response['message']['bot_id']
            return
        except Exception as e:
            await self.logger.error(f'Error in send_message: {e}')
            raise e

    async def send_message_to_one(self, text: str, user_id: str):
        try:
            response = await self.client.chat_postMessage(channel='@' + user_id, text=text)
            if self.bot_user_id is None and response.get('ok'):
                self.bot_user_id = response['message']['bot_id']

            return
        except Exception as e:
            await self.logger.error(f'Error in send_message: {traceback.format_exc()}')
            raise e

    async def run_task(self, host: str, port: int, *args, **kwargs):
        """
        启动 Quart 应用。
        """
        await self.app.run_task(host=host, port=port, *args, **kwargs)
