"""
斜杠命令系统
实现类似 Dundun 的命令处理
"""

from typing import Optional, Dict, Any, Callable, List, Awaitable
from dataclasses import dataclass, field
from enum import Enum
import asyncio


class CommandCategory(str, Enum):
    """命令分类"""
    SESSION = "session"
    AGENT = "agent"
    TOOL = "tool"
    SYSTEM = "system"
    DEBUG = "debug"


@dataclass
class CommandInfo:
    """命令信息"""
    name: str
    description: str
    category: CommandCategory
    aliases: List[str] = field(default_factory=list)
    usage: str = ""
    hidden: bool = False
    needs_args: bool = False  # 是否需要参数（需要则放入输入框让用户填写）


@dataclass
class CommandResult:
    """命令执行结果"""
    success: bool
    message: str = ""
    data: Any = None
    should_exit: bool = False
    should_continue: bool = True  # 是否继续主循环
    fill_input: str = ""  # 将命令填入输入框让用户填写参数


class CommandHandler:
    """命令处理器基类"""

    def __init__(self):
        self.commands: Dict[str, CommandInfo] = {}
        self.handlers: Dict[str, Callable] = {}
        self._register_builtin_commands()

    def _register_builtin_commands(self):
        """注册内置命令"""
        # 系统命令
        self.register(
            CommandInfo(
                name="help",
                description="显示帮助信息，可指定命令名",
                category=CommandCategory.SYSTEM,
                aliases=["h", "?"],
                usage="/help [command]",
            ),
            self._cmd_help
        )

        self.register(
            CommandInfo(
                name="exit",
                description="退出当前会话并关闭程序",
                category=CommandCategory.SYSTEM,
                aliases=["quit", "q"],
            ),
            self._cmd_exit
        )

        self.register(
            CommandInfo(
                name="clear",
                description="新建会话并清空输出",
                category=CommandCategory.SYSTEM,
                aliases=["cls"],
            ),
            self._cmd_clear
        )

        # 会话命令
        self.register(
            CommandInfo(
                name="session",
                description="查看会话列表",
                category=CommandCategory.SESSION,
                aliases=["s"],
                usage="/session",
            ),
            self._cmd_session
        )

        self.register(
            CommandInfo(
                name="compact",
                description="压缩上下文，释放 token 空间",
                category=CommandCategory.SESSION,
            ),
            self._cmd_compact
        )

        # Agent 命令
        self.register(
            CommandInfo(
                name="mode",
                description="切换 Agent: build/plan/explore/general/web",
                category=CommandCategory.AGENT,
                aliases=["agent"],
                usage="/mode <build|plan|general|explore|web>",
            ),
            self._cmd_mode
        )

        self.register(
            CommandInfo(
                name="model",
                description="切换 LLM 模型",
                category=CommandCategory.AGENT,
                usage="/model [model_name]",
            ),
            self._cmd_model
        )

        self.register(
            CommandInfo(
                name="status",
                description="显示模型、Agent、上下文等状态",
                category=CommandCategory.SYSTEM,
            ),
            self._cmd_status
        )

        # 工具命令
        self.register(
            CommandInfo(
                name="tools",
                description="列出当前可用的所有工具",
                category=CommandCategory.TOOL,
            ),
            self._cmd_tools
        )

        self.register(
            CommandInfo(
                name="todo",
                description="显示 Agent 的待办事项列表",
                category=CommandCategory.TOOL,
                aliases=["todos"],
            ),
            self._cmd_todo
        )

        # 项目命令
        self.register(
            CommandInfo(
                name="init",
                description="生成 AGENT.md，--force 覆盖已有",
                category=CommandCategory.SYSTEM,
                usage="/init [--force]",
            ),
            self._cmd_init
        )

        # 调试命令
        self.register(
            CommandInfo(
                name="debug",
                description="开启/关闭调试日志输出",
                category=CommandCategory.DEBUG,
                hidden=True,
            ),
            self._cmd_debug
        )

        self.register(
            CommandInfo(
                name="config",
                description="显示 Provider、模型等配置详情",
                category=CommandCategory.DEBUG,
                hidden=True,
            ),
            self._cmd_config
        )

        # Skill 命令
        self.register(
            CommandInfo(
                name="skills",
                description="列出可用的自定义 Skill 脚本",
                category=CommandCategory.TOOL,
            ),
            self._cmd_skills
        )

    def register(self, info: CommandInfo, handler: Callable):
        """注册命令"""
        self.commands[info.name] = info
        self.handlers[info.name] = handler

        # 注册别名
        for alias in info.aliases:
            self.commands[alias] = info
            self.handlers[alias] = handler

    def parse(self, input_text: str) -> tuple:
        """解析命令输入"""
        if not input_text.startswith("/"):
            return None, None, []

        parts = input_text[1:].split()
        if not parts:
            return None, None, []

        cmd_name = parts[0].lower()
        args = parts[1:] if len(parts) > 1 else []

        return cmd_name, self.commands.get(cmd_name), args

    async def execute(self, input_text: str, context: Dict[str, Any] = None) -> Optional[CommandResult]:
        """执行命令"""
        cmd_name, cmd_info, args = self.parse(input_text)

        if cmd_name is None:
            return None

        # 命令不存在：返回 None 让调用方将其作为普通 prompt 处理
        if cmd_info is None:
            return None

        # 如果命令需要参数但没有提供，填充到输入框
        if cmd_info.needs_args and not args:
            return CommandResult(
                success=True,
                message="",
                fill_input=f"/{cmd_info.name} "
            )

        handler = self.handlers.get(cmd_info.name)
        if handler is None:
            return CommandResult(
                success=False,
                message=f"命令 /{cmd_name} 未实现"
            )

        try:
            if asyncio.iscoroutinefunction(handler):
                return await handler(args, context or {})
            else:
                return handler(args, context or {})
        except Exception as e:
            return CommandResult(
                success=False,
                message=f"命令执行错误: {str(e)}"
            )

    # ========== 内置命令实现 ==========

    def _cmd_help(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """帮助命令"""
        if args:
            # 显示特定命令的帮助
            cmd_name = args[0].lower()
            cmd_info = self.commands.get(cmd_name)
            if cmd_info:
                lines = [
                    f"命令: /{cmd_info.name}",
                    f"描述: {cmd_info.description}",
                ]
                if cmd_info.aliases:
                    lines.append(f"别名: {', '.join('/' + a for a in cmd_info.aliases)}")
                if cmd_info.usage:
                    lines.append(f"用法: {cmd_info.usage}")
                return CommandResult(success=True, message="\n".join(lines))
            else:
                return CommandResult(success=False, message=f"未知命令: {cmd_name}")

        # 显示所有命令
        lines = ["可用命令:\n"]

        # 按分类组织
        categories = {}
        for name, info in self.commands.items():
            if info.hidden:
                continue
            if name != info.name:  # 跳过别名
                continue
            if info.category not in categories:
                categories[info.category] = []
            categories[info.category].append(info)

        category_names = {
            CommandCategory.SYSTEM: "系统",
            CommandCategory.SESSION: "会话",
            CommandCategory.AGENT: "Agent",
            CommandCategory.TOOL: "工具",
            CommandCategory.DEBUG: "调试",
        }

        for category, cmds in categories.items():
            lines.append(f"\n[{category_names.get(category, category)}]")
            for cmd in cmds:
                aliases = f" ({', '.join('/' + a for a in cmd.aliases)})" if cmd.aliases else ""
                lines.append(f"  /{cmd.name}{aliases} - {cmd.description}")

        return CommandResult(success=True, message="\n".join(lines))

    def _cmd_exit(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """退出命令"""
        return CommandResult(
            success=True,
            message="",  # 不在这里输出，由 cli.py 的 finally 块统一输出
            should_exit=True,
            should_continue=False
        )

    def _cmd_clear(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """清除命令：创建全新会话并清空输出"""
        return CommandResult(
            success=True,
            message="",
            data={"new_session": True}
        )

    def _cmd_session(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """会话切换命令"""
        current_session_id = context.get("session_id", "")

        if not args:
            # 无参数：通过 CommandList 子菜单选择，不应走到这里
            return CommandResult(success=True, message="请通过命令菜单选择会话")

        session_id = args[0]

        # 和当前会话相同，不做任何处理
        if session_id == current_session_id:
            return CommandResult(success=True, message="")

        return CommandResult(
            success=True,
            message=None,
            data={"switch_session_id": session_id}
        )

    def _cmd_compact(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """压缩上下文命令（通过 WebSocket 发送请求）"""
        # 通过 WebSocket 发送压缩请求，显示由 renderer 处理
        return CommandResult(
            success=True,
            message="",
            data={"compact": True}
        )

    def _cmd_mode(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """切换 Agent 模式"""
        if not args:
            current_agent = context.get("current_agent", "general")
            return CommandResult(
                success=True,
                message=f"当前模式: {current_agent}\n可用模式: build, plan, general, explore, web"
            )

        mode = args[0].lower()
        valid_modes = ["build", "plan", "general", "explore", "web"]

        if mode not in valid_modes:
            return CommandResult(
                success=False,
                message=f"无效模式: {mode}\n可用模式: {', '.join(valid_modes)}"
            )

        return CommandResult(
            success=True,
            message=f"切换到 {mode} 模式",
            data={"switch_agent": mode}
        )

    def _cmd_model(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """切换模型"""
        available_models = context.get("available_models", [])
        current_model = context.get("model_name", "未知")

        if not args:
            # 显示当前模型和可用模型列表
            lines = [f"当前模型: {current_model}"]
            if available_models:
                lines.append("可用模型:")
                for m in available_models:
                    marker = " *" if m == current_model else ""
                    lines.append(f"  {m}{marker}")
            else:
                lines.append("（模型列表不可用，请在 config.json 中配置）")
            return CommandResult(success=True, message="\n".join(lines))

        model_name = args[0]

        # 验证模型名称
        if available_models and model_name not in available_models:
            return CommandResult(
                success=False,
                message=f"未知模型: {model_name}\n可用模型: {', '.join(available_models)}"
            )

        return CommandResult(
            success=True,
            message=None,
            data={"switch_model": model_name}
        )

    def _cmd_status(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """显示状态"""
        lines = ["当前状态:\n"]

        # Agent
        current_agent = context.get("current_agent", "general")
        lines.append(f"Agent: {current_agent}")

        # 会话
        session_id = context.get("session_id")
        if session_id:
            lines.append(f"会话 ID: {session_id[:8]}...")

        # 模型
        model_name = context.get("model_name", "未知")
        provider = context.get("provider", "未知")
        lines.append(f"模型: {provider}/{model_name}")

        # 工具数量
        tool_registry = context.get("tool_registry")
        if tool_registry:
            tools = tool_registry.get_all_tools()
            lines.append(f"工具数量: {len(tools)}")

        return CommandResult(success=True, message="\n".join(lines))

    def _cmd_tools(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """列出当前会话可用的工具（通过 WebSocket 查询）"""
        # 返回标记，让 cli.py 处理 WebSocket 消息
        return CommandResult(
            success=True,
            message="",
            data={"query_tools": True}
        )

    def _cmd_todo(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """显示待办事项（通过 WebSocket 查询）"""
        # 返回标记，让 cli.py 处理 WebSocket 消息
        return CommandResult(
            success=True,
            message="",
            data={"query_todo": True}
        )

    def _cmd_debug(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """调试模式"""
        debug_mode = context.get("debug_mode", False)
        new_mode = not debug_mode
        return CommandResult(
            success=True,
            message=f"调试模式: {'开启' if new_mode else '关闭'}",
            data={"debug_mode": new_mode}
        )

    def _cmd_config(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """显示配置"""
        config = context.get("config")
        if not config:
            return CommandResult(success=False, message="配置未加载")

        import json
        try:
            config_str = json.dumps(config, indent=2, default=str)
            return CommandResult(success=True, message=f"配置:\n{config_str}")
        except:
            return CommandResult(success=True, message=str(config))

    def _cmd_init(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """初始化项目，生成 AGENT.md"""
        # 解析参数
        force = "--force" in args or "-f" in args

        # 返回标记，让服务端处理
        return CommandResult(
            success=True,
            message="",
            data={"init": True, "force": force},
        )

    def _cmd_skills(self, args: List[str], context: Dict[str, Any]) -> CommandResult:
        """列出可用的 skills（通过 WebSocket 查询）"""
        # 返回标记，让 cli.py 处理 WebSocket 消息
        return CommandResult(
            success=True,
            message="",
            data={"query_skills": True}
        )


# 全局命令处理器
_command_handler: Optional[CommandHandler] = None


def get_command_handler() -> CommandHandler:
    """获取全局命令处理器"""
    global _command_handler
    if _command_handler is None:
        _command_handler = CommandHandler()
    return _command_handler
