from typing import Callable, List, Optional, Type

from app.agent.tools.impl.add_download_tasks import AddDownloadTasksTool
from app.agent.tools.impl.add_subscribe import AddSubscribeTool
from app.agent.tools.impl.update_subscribe import UpdateSubscribeTool
from app.agent.tools.impl.search_subscribe import SearchSubscribeTool
from app.agent.tools.impl.get_recommendations import GetRecommendationsTool
from app.agent.tools.impl.query_downloaders import QueryDownloadersTool
from app.agent.tools.impl.query_download_tasks import QueryDownloadTasksTool
from app.agent.tools.impl.query_library_exists import QueryLibraryExistsTool
from app.agent.tools.impl.query_library_latest import QueryLibraryLatestTool
from app.agent.tools.impl.query_sites import QuerySitesTool
from app.agent.tools.impl.update_site import UpdateSiteTool
from app.agent.tools.impl.query_site_userdata import QuerySiteUserdataTool
from app.agent.tools.impl.test_site import TestSiteTool
from app.agent.tools.impl.query_subscribes import QuerySubscribesTool
from app.agent.tools.impl.query_subscribe_shares import QuerySubscribeSharesTool
from app.agent.tools.impl.query_rule_groups import QueryRuleGroupsTool
from app.agent.tools.impl.query_builtin_filter_rules import QueryBuiltinFilterRulesTool
from app.agent.tools.impl.query_custom_filter_rules import QueryCustomFilterRulesTool
from app.agent.tools.impl.add_custom_filter_rule import AddCustomFilterRuleTool
from app.agent.tools.impl.update_custom_filter_rule import UpdateCustomFilterRuleTool
from app.agent.tools.impl.delete_custom_filter_rule import DeleteCustomFilterRuleTool
from app.agent.tools.impl.add_rule_group import AddRuleGroupTool
from app.agent.tools.impl.update_rule_group import UpdateRuleGroupTool
from app.agent.tools.impl.delete_rule_group import DeleteRuleGroupTool
from app.agent.tools.impl.query_popular_subscribes import QueryPopularSubscribesTool
from app.agent.tools.impl.query_subscribe_history import QuerySubscribeHistoryTool
from app.agent.tools.impl.delete_subscribe import DeleteSubscribeTool
from app.agent.tools.impl.search_media import SearchMediaTool
from app.agent.tools.impl.search_person import SearchPersonTool
from app.agent.tools.impl.search_person_credits import SearchPersonCreditsTool
from app.agent.tools.impl.recognize_media import RecognizeMediaTool
from app.agent.tools.impl.scrape_metadata import ScrapeMetadataTool
from app.agent.tools.impl.query_episode_schedule import QueryEpisodeScheduleTool
from app.agent.tools.impl.query_media_detail import QueryMediaDetailTool
from app.agent.tools.impl.search_torrents import SearchTorrentsTool
from app.agent.tools.impl.get_search_results import GetSearchResultsTool
from app.agent.tools.impl.search_web import SearchWebTool
from app.agent.tools.impl.recognize_captcha import RecognizeCaptchaTool
from app.agent.tools.impl.send_message import SendMessageTool
from app.agent.tools.impl.ask_user_choice import AskUserChoiceTool
from app.agent.tools.impl.send_local_file import SendLocalFileTool
from app.agent.tools.impl.send_voice_message import SendVoiceMessageTool
from app.agent.tools.impl.create_agent_task import CreateAgentTaskTool
from app.agent.tools.impl.delete_agent_task import DeleteAgentTaskTool
from app.agent.tools.impl.query_agent_tasks import QueryAgentTasksTool
from app.agent.tools.impl.query_schedulers import QuerySchedulersTool
from app.agent.tools.impl.run_scheduler import RunSchedulerTool
from app.agent.tools.impl.update_agent_task import UpdateAgentTaskTool
from app.agent.tools.impl.query_workflows import QueryWorkflowsTool
from app.agent.tools.impl.run_workflow import RunWorkflowTool
from app.agent.tools.impl.query_personas import QueryPersonasTool
from app.agent.tools.impl.switch_persona import SwitchPersonaTool
from app.agent.tools.impl.update_persona_definition import UpdatePersonaDefinitionTool
from app.agent.tools.impl.update_site_cookie import UpdateSiteCookieTool
from app.agent.tools.impl.delete_download_tasks import DeleteDownloadTasksTool
from app.agent.tools.impl.delete_download_history import DeleteDownloadHistoryTool
from app.agent.tools.impl.delete_transfer_history import DeleteTransferHistoryTool
from app.agent.tools.impl.update_download_tasks import UpdateDownloadTasksTool
from app.agent.tools.impl.query_directory_settings import QueryDirectorySettingsTool
from app.agent.tools.impl.list_directory import ListDirectoryTool
from app.agent.tools.impl.query_transfer_history import QueryTransferHistoryTool
from app.agent.tools.impl.transfer_file import TransferFileTool
from app.agent.tools.impl.execute_command import ExecuteCommandTool
from app.agent.tools.impl.edit_file import EditFileTool
from app.agent.tools.impl.write_file import WriteFileTool
from app.agent.tools.impl.read_file import ReadFileTool
from app.agent.tools.impl.browse_webpage import BrowseWebpageTool
from app.agent.tools.impl.query_installed_plugins import QueryInstalledPluginsTool
from app.agent.tools.impl.query_market_plugins import QueryMarketPluginsTool
from app.agent.tools.impl.query_plugin_capabilities import QueryPluginCapabilitiesTool
from app.agent.tools.impl.query_plugin_config import QueryPluginConfigTool
from app.agent.tools.impl.update_plugin_config import UpdatePluginConfigTool
from app.agent.tools.impl.reload_plugin import ReloadPluginTool
from app.agent.tools.impl.query_plugin_data import QueryPluginDataTool
from app.agent.tools.impl.install_plugin import InstallPluginTool
from app.agent.tools.impl.uninstall_plugin import UninstallPluginTool
from app.agent.tools.impl.run_slash_command import RunSlashCommandTool
from app.agent.tools.impl.list_slash_commands import ListSlashCommandsTool
from app.agent.tools.impl.query_custom_identifiers import QueryCustomIdentifiersTool
from app.agent.tools.impl.query_doctor_report import QueryDoctorReportTool
from app.agent.tools.impl.update_custom_identifiers import UpdateCustomIdentifiersTool
from app.agent.tools.impl.query_system_settings import QuerySystemSettingsTool
from app.agent.tools.impl.update_system_settings import UpdateSystemSettingsTool
from app.agent.llm.capability import AgentCapabilityManager
from app.core.plugin import PluginManager
from app.log import logger
from app.schemas.message import ChannelCapabilityManager
from app.schemas.types import MessageChannel
from .base import MoviePilotTool


class MoviePilotToolFactory:
    """
    MoviePilot工具工厂
    """

    BUILTIN_TOOL_CLASSES: tuple[Type[MoviePilotTool], ...] = (
        SearchMediaTool,
        SearchPersonTool,
        SearchPersonCreditsTool,
        RecognizeMediaTool,
        ScrapeMetadataTool,
        QueryEpisodeScheduleTool,
        QueryMediaDetailTool,
        AddSubscribeTool,
        UpdateSubscribeTool,
        SearchSubscribeTool,
        SearchTorrentsTool,
        GetSearchResultsTool,
        SearchWebTool,
        RecognizeCaptchaTool,
        AddDownloadTasksTool,
        QuerySubscribesTool,
        QuerySubscribeSharesTool,
        QueryPopularSubscribesTool,
        QueryBuiltinFilterRulesTool,
        QueryCustomFilterRulesTool,
        QueryRuleGroupsTool,
        AddCustomFilterRuleTool,
        UpdateCustomFilterRuleTool,
        DeleteCustomFilterRuleTool,
        AddRuleGroupTool,
        UpdateRuleGroupTool,
        DeleteRuleGroupTool,
        QuerySubscribeHistoryTool,
        DeleteSubscribeTool,
        QueryDownloadTasksTool,
        DeleteDownloadTasksTool,
        DeleteDownloadHistoryTool,
        DeleteTransferHistoryTool,
        UpdateDownloadTasksTool,
        QueryDownloadersTool,
        QuerySitesTool,
        UpdateSiteTool,
        QuerySiteUserdataTool,
        TestSiteTool,
        UpdateSiteCookieTool,
        GetRecommendationsTool,
        QueryLibraryExistsTool,
        QueryLibraryLatestTool,
        QueryDirectorySettingsTool,
        ListDirectoryTool,
        QueryTransferHistoryTool,
        TransferFileTool,
        SendMessageTool,
        CreateAgentTaskTool,
        QueryAgentTasksTool,
        UpdateAgentTaskTool,
        DeleteAgentTaskTool,
        QuerySchedulersTool,
        RunSchedulerTool,
        QueryWorkflowsTool,
        RunWorkflowTool,
        QueryPersonasTool,
        SwitchPersonaTool,
        UpdatePersonaDefinitionTool,
        ExecuteCommandTool,
        EditFileTool,
        WriteFileTool,
        ReadFileTool,
        BrowseWebpageTool,
        QueryInstalledPluginsTool,
        QueryMarketPluginsTool,
        QueryPluginCapabilitiesTool,
        QueryPluginConfigTool,
        UpdatePluginConfigTool,
        ReloadPluginTool,
        QueryPluginDataTool,
        InstallPluginTool,
        UninstallPluginTool,
        RunSlashCommandTool,
        ListSlashCommandsTool,
        QueryDoctorReportTool,
        QueryCustomIdentifiersTool,
        UpdateCustomIdentifiersTool,
        QuerySystemSettingsTool,
        UpdateSystemSettingsTool,
    )

    # 这些通用工具需要始终保留，避免大工具集裁剪后让 Agent 丢失基础的
    # 文件系统、命令执行、历史检索或交互确认能力。AskUserChoiceTool 仅在支持按钮
    # 的渠道中才会实际注入，因此后续会再按已加载工具做一次求交集。
    TOOL_SELECTOR_ALWAYS_INCLUDE_NAMES = (
        "list_directory",
        "write_file",
        "read_file",
        "edit_file",
        "execute_command",
        "ask_user_choice",
        "create_agent_task",
        "query_agent_tasks",
    )

    @staticmethod
    def _should_enable_choice_tool(channel: Optional[str] = None) -> bool:
        if not channel:
            return False
        try:
            message_channel = MessageChannel(channel)
        except ValueError:
            return False
        return ChannelCapabilityManager.supports_buttons(
            message_channel
        ) and ChannelCapabilityManager.supports_callbacks(message_channel)

    @classmethod
    def get_tool_selector_always_include_names(
        cls, tools: List[MoviePilotTool]
    ) -> List[str]:
        """
        返回当前实际已加载且需要绕过工具筛选的工具名。

        `LLMToolSelectorMiddleware` 会校验 `always_include` 中的工具名是否
        存在于当前请求里，因此这里必须根据运行时工具列表做交集过滤。
        """
        available_tool_names = {
            tool.name for tool in tools if getattr(tool, "name", None)
        }
        return [
            tool_name
            for tool_name in cls.TOOL_SELECTOR_ALWAYS_INCLUDE_NAMES
            if tool_name in available_tool_names
        ]

    @classmethod
    def _get_builtin_tool_classes(
        cls, channel: Optional[str] = None
    ) -> list[Type[MoviePilotTool]]:
        """
        返回当前渠道可用的内置工具类清单。
        """
        tool_definitions = list(cls.BUILTIN_TOOL_CLASSES)
        if cls._should_enable_choice_tool(channel):
            tool_definitions.append(AskUserChoiceTool)
        tool_definitions.append(SendLocalFileTool)
        if AgentCapabilityManager.supports_audio_output():
            tool_definitions.append(SendVoiceMessageTool)
        return tool_definitions

    @classmethod
    def create_tools(
        cls,
        session_id: str,
        user_id: str,
        channel: str = None,
        source: str = None,
        username: str = None,
        stream_handler: Callable = None,
        agent_context: dict = None,
        allow_message_tools: bool = True,
    ) -> List[MoviePilotTool]:
        """
        创建MoviePilot工具列表
        """
        tools = []
        tool_definitions = cls._get_builtin_tool_classes(channel)
        # 创建内置工具
        for ToolClass in tool_definitions:
            tool = ToolClass(session_id=session_id, user_id=user_id)
            if not allow_message_tools and getattr(tool, "sends_message", False):
                continue
            tool.set_message_attr(channel=channel, source=source, username=username)
            tool.set_stream_handler(stream_handler=stream_handler)
            tool.set_agent_context(agent_context=agent_context)
            tools.append(tool)

        # 加载插件提供的工具
        plugin_tools_count = 0
        plugin_tools_info = PluginManager().get_plugin_agent_tools()
        for plugin_info in plugin_tools_info:
            plugin_id = plugin_info.get("plugin_id")
            plugin_name = plugin_info.get("plugin_name")
            tool_classes = plugin_info.get("tools", [])
            for ToolClass in tool_classes:
                try:
                    # 验证工具类是否继承自 MoviePilotTool
                    if not issubclass(ToolClass, MoviePilotTool):
                        logger.warning(
                            f"插件 {plugin_name}({plugin_id}) 提供的工具类 {ToolClass.__name__} 未继承自 MoviePilotTool，已跳过"
                        )
                        continue
                    # 创建工具实例
                    tool = ToolClass(session_id=session_id, user_id=user_id)
                    if not allow_message_tools and getattr(tool, "sends_message", False):
                        continue
                    tool.set_message_attr(
                        channel=channel, source=source, username=username
                    )
                    tool.set_stream_handler(stream_handler=stream_handler)
                    tool.set_agent_context(agent_context=agent_context)
                    tools.append(tool)
                    plugin_tools_count += 1
                    logger.debug(
                        f"成功加载插件 {plugin_name}({plugin_id}) 的工具: {ToolClass.__name__}"
                    )
                except Exception as e:
                    logger.error(
                        f"加载插件 {plugin_name}({plugin_id}) 的工具 {ToolClass.__name__} 失败: {str(e)}"
                    )

        builtin_tools_count = len(tool_definitions)
        if plugin_tools_count > 0:
            logger.debug(
                f"成功创建 {len(tools)} 个MoviePilot工具（内置工具: {builtin_tools_count} 个，插件工具: {plugin_tools_count} 个）"
            )
        else:
            logger.debug(f"成功创建 {len(tools)} 个MoviePilot工具")
        return tools
