import re
from threading import Lock
from typing import List, Optional

import requests
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from slack_sdk import WebClient

from app.core.config import settings
from app.core.context import MediaInfo, Context
from app.core.metainfo import MetaInfo
from app.log import logger
from app.utils.string import StringUtils


lock = Lock()


class Slack:

    _client: WebClient = None
    _service: SocketModeHandler = None

    _ds_url = f"http://127.0.0.1:{settings.PORT}/api/v1/message?token={settings.API_TOKEN}"

    def __init__(self):

        if not settings.SLACK_OAUTH_TOKEN or not settings.SLACK_APP_TOKEN:
            return
        
        try:
            slack_app = App(token=settings.SLACK_OAUTH_TOKEN,
                            ssl_check_enabled=False,
                            url_verification_enabled=False)
        except Exception as err:
            logger.error(f"Slack初始化失败: {err}")
            return
        self._client = slack_app.client

        # 注册消息响应
        @slack_app.event("message")
        def slack_message(message):
            local_res = requests.post(self._ds_url, json=message, timeout=10)
            logger.debug("message: %s processed, response is: %s" % (message, local_res.text))

        @slack_app.action(re.compile(r"actionId-\d+"))
        def slack_action(ack, body):
            ack()
            local_res = requests.post(self._ds_url, json=body, timeout=60)
            logger.debug("message: %s processed, response is: %s" % (body, local_res.text))

        @slack_app.event("app_mention")
        def slack_mention(say, body):
            say(f"收到，请稍等... <@{body.get('event', {}).get('user')}>")
            local_res = requests.post(self._ds_url, json=body, timeout=10)
            logger.debug("message: %s processed, response is: %s" % (body, local_res.text))

        @slack_app.shortcut(re.compile(r"/*"))
        def slack_shortcut(ack, body):
            ack()
            local_res = requests.post(self._ds_url, json=body, timeout=10)
            logger.debug("message: %s processed, response is: %s" % (body, local_res.text))

        @slack_app.command(re.compile(r"/*"))
        def slack_command(ack, body):
            ack()
            local_res = requests.post(self._ds_url, json=body, timeout=10)
            logger.debug("message: %s processed, response is: %s" % (body, local_res.text))

        # 启动服务
        try:
            self._service = SocketModeHandler(
                slack_app,
                settings.SLACK_APP_TOKEN
            )
            self._service.connect()
            logger.info("Slack消息接收服务启动")
        except Exception as err:
            logger.error("Slack消息接收服务启动失败: %s" % str(err))

    def stop(self):
        if self._service:
            try:
                self._service.close()
                logger.info("Slack消息接收服务已停止")
            except Exception as err:
                logger.error("Slack消息接收服务停止失败: %s" % str(err))

    def send_msg(self, title: str, text: str = "", image: str = "", url: str = "", userid: str = ""):
        """
        发送Telegram消息
        :param title: 消息标题
        :param text: 消息内容
        :param image: 消息图片地址
        :param url: 点击消息转转的URL
        :param userid: 用户ID，如有则只发消息给该用户
        :user_id: 发送消息的目标用户ID，为空则发给管理员
        """
        if not self._client:
            return False, "消息客户端未就绪"
        if not title and not text:
            return False, "标题和内容不能同时为空"
        try:
            if userid:
                channel = userid
            else:
                # 消息广播
                channel = self.__find_public_channel()
            # 消息文本
            message_text = ""
            # 结构体
            blocks = []
            if not image:
                message_text = f"{title}\n{text or ''}"
            else:
                # 消息图片
                if image:
                    # 拼装消息内容
                    blocks.append({"type": "section", "text": {
                        "type": "mrkdwn",
                        "text": f"*{title}*\n{text or ''}"
                    }, 'accessory': {
                        "type": "image",
                        "image_url": f"{image}",
                        "alt_text": f"{title}"
                    }})
                # 链接
                if url:
                    blocks.append({
                        "type": "actions",
                        "elements": [
                            {
                                "type": "button",
                                "text": {
                                    "type": "plain_text",
                                    "text": "查看详情",
                                    "emoji": True
                                },
                                "value": "click_me_url",
                                "url": f"{url}",
                                "action_id": "actionId-url"
                            }
                        ]
                    })
            # 发送
            result = self._client.chat_postMessage(
                channel=channel,
                text=message_text[:1000],
                blocks=blocks,
                mrkdwn=True
            )
            return True, result
        except Exception as msg_e:
            logger.error(f"Slack消息发送失败: {msg_e}")
            return False, str(msg_e)

    def send_meidas_msg(self, medias: List[MediaInfo], userid: str = "", title: str = "") -> Optional[bool]:
        """
        发送列表类消息
        """
        if not self._client:
            return False
        if not medias:
            return False
        try:
            if userid:
                channel = userid
            else:
                # 消息广播
                channel = self.__find_public_channel()
            # 消息主体
            title_section = {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{title}*"
                }
            }
            blocks = [title_section]
            # 列表
            if medias:
                blocks.append({
                    "type": "divider"
                })
                index = 1
                for media in medias:
                    if media.get_poster_image():
                        if media.vote_star:
                            text = f"{index}. *<{media.detail_link}|{media.title_year}>*" \
                                   f"\n类型：{media.type.value}" \
                                   f"\n{media.vote_star}" \
                                   f"\n{media.get_overview_string(50)}"
                        else:
                            text = f"{index}. *<{media.detail_link}|{media.title_year}>*" \
                                   f"\n类型：{media.type.value}" \
                                   f"\n{media.get_overview_string(50)}"
                        blocks.append(
                            {
                                "type": "section",
                                "text": {
                                    "type": "mrkdwn",
                                    "text": text
                                },
                                "accessory": {
                                    "type": "image",
                                    "image_url": f"{media.get_poster_image()}",
                                    "alt_text": f"{media.title_year}"
                                }
                            }
                        )
                        blocks.append(
                            {
                                "type": "actions",
                                "elements": [
                                    {
                                        "type": "button",
                                        "text": {
                                            "type": "plain_text",
                                            "text": "选择",
                                            "emoji": True
                                        },
                                        "value": f"{index}",
                                        "action_id": f"actionId-{index}"
                                    }
                                ]
                            }
                        )
                        index += 1
            # 发送
            result = self._client.chat_postMessage(
                channel=channel,
                text=title,
                blocks=blocks
            )
            return True if result else False
        except Exception as msg_e:
            logger.error(f"Slack消息发送失败: {msg_e}")
            return False

    def send_torrents_msg(self, torrents: List[Context],
                          userid: str = "", title: str = "") -> Optional[bool]:
        """
        发送列表消息
        """
        if not self._client:
            return None

        try:
            if userid:
                channel = userid
            else:
                # 消息广播
                channel = self.__find_public_channel()
            # 消息主体
            title_section = {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{title}*"
                }
            }
            blocks = [title_section, {
                "type": "divider"
            }]
            # 列表
            index = 1
            for context in torrents:
                torrent = context.torrent_info
                site_name = torrent.site_name
                meta = MetaInfo(torrent.title, torrent.description)
                link = torrent.page_url
                title = f"{meta.season_episode} " \
                        f"{meta.resource_term} " \
                        f"{meta.release_group}"
                title = re.sub(r"\s+", " ", title).strip()
                free = torrent.volume_factor
                seeder = f"{torrent.seeders}↑"
                description = torrent.description
                text = f"{index}. 【{site_name}】<{link}|{title}> " \
                       f"{StringUtils.str_filesize(torrent.size)} {free} {seeder}\n" \
                       f"{description}"
                blocks.append(
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": text
                        }
                    }
                )
                blocks.append(
                    {
                        "type": "actions",
                        "elements": [
                            {
                                "type": "button",
                                "text": {
                                    "type": "plain_text",
                                    "text": "选择",
                                    "emoji": True
                                },
                                "value": f"{index}",
                                "action_id": f"actionId-{index}"
                            }
                        ]
                    }
                )
                index += 1
            # 发送
            result = self._client.chat_postMessage(
                channel=channel,
                text=title,
                blocks=blocks
            )
            return True if result else False
        except Exception as msg_e:
            logger.error(f"Slack消息发送失败: {msg_e}")
            return False

    def __find_public_channel(self):
        """
        查找公共频道
        """
        if not self._client:
            return ""
        conversation_id = ""
        try:
            for result in self._client.conversations_list():
                if conversation_id:
                    break
                for channel in result["channels"]:
                    if channel.get("name") == (settings.SLACK_CHANNEL or "全体"):
                        conversation_id = channel.get("id")
                        break
        except Exception as e:
            logger.error(f"查找Slack公共频道失败: {e}")
        return conversation_id
