from __future__ import annotations

from io import BytesIO

from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters

from .agent import IntentParser
from .config import Settings
from .control import ControlBackend
from .transcriber import VoiceTranscriber


class TelegramBotService:
    def __init__(
        self,
        settings: Settings,
        worker: ControlBackend,
        transcriber: VoiceTranscriber,
        intent_parser: IntentParser,
    ) -> None:
        self.settings = settings
        self.worker = worker
        self.transcriber = transcriber
        self.intent_parser = intent_parser
        self.application = Application.builder().token(settings.telegram_bot_token).build()
        self.application.add_handler(CommandHandler("help", self.help))
        self.application.add_handler(CommandHandler("start", self.start))
        self.application.add_handler(CommandHandler("help_zh", self.help_zh))
        self.application.add_handler(CommandHandler("help_voice", self.help_voice))
        self.application.add_handler(CommandHandler("diagnose_zh", self.diagnose_zh))
        self.application.add_handler(CommandHandler("status", self.status))
        self.application.add_handler(CommandHandler("continue", self.continue_task))
        self.application.add_handler(CommandHandler("tasks", self.tasks))
        self.application.add_handler(CommandHandler("newtask", self.new_task))
        self.application.add_handler(CommandHandler("switchtask", self.switch_task))
        self.application.add_handler(CommandHandler("pause", self.pause))
        self.application.add_handler(CommandHandler("note", self.note))
        self.application.add_handler(CommandHandler("run", self.run_command))
        self.application.add_handler(CommandHandler("confirmrun", self.confirm_run))
        self.application.add_handler(CommandHandler("cancelrun", self.cancel_run))
        self.application.add_handler(CommandHandler("approve", self.approve))
        self.application.add_handler(CommandHandler("reject", self.reject))
        self.application.add_handler(CommandHandler("setworkdir", self.set_workdir))
        self.application.add_handler(CommandHandler("settitle", self.set_title))
        self.application.add_handler(CommandHandler("notes", self.notes))
        self.application.add_handler(CommandHandler("queue", self.queue))
        self.application.add_handler(CommandHandler("progress", self.progress))
        self.application.add_handler(CommandHandler("history", self.history))
        self.application.add_handler(CommandHandler("tail", self.tail))
        self.application.add_handler(CommandHandler("agent", self.agent))
        self.application.add_handler(CommandHandler("reset", self.reset))
        self.application.add_handler(MessageHandler(filters.VOICE, self.handle_voice))
        self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_text))

    async def ensure_allowed(self, update: Update) -> bool:
        user = update.effective_user
        if user is None or user.id not in self.settings.allowed_user_ids:
            if update.effective_message is not None:
                await update.effective_message.reply_text("This bot is not enabled for this Telegram account.")
            return False
        return True

    async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(
            update.effective_message,
            "CodexBot 已上线。\n"
            "可用命令：/help /help_zh /help_voice /diagnose_zh /status /tasks /newtask /switchtask /continue /pause /note /run /approve /reject /setworkdir /settitle /notes /queue /progress /history /tail /agent /reset\n"
            "你也可以直接发送文字，或发送语音消息。",
        )

    async def help(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(
            update.effective_message,
            "\n".join(
                [
                    "CodexBot 帮助总览：",
                    "/help_zh：中文操作速查",
                    "/help_voice：语音使用说明",
                    "/diagnose_zh 你的句子：查看一句中文会被如何理解",
                    "/status：当前任务状态",
                    "/tasks：任务列表",
                    "/queue：当前队列",
                    "/progress：当前进度",
                    "你也可以直接发送中文文字或语音，bot 会尽量自动理解。",
                ]
            ),
        )

    async def help_zh(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(
            update.effective_message,
            "\n".join(
                [
                    "中文操作速查：",
                    "看状态：看看任务状态 / 查看任务列表 / 看看队列 / 看看进度",
                    "看结果：看看最近输出 / 看看历史 / 看看备注 / 看看待审批命令",
                    "任务管理：新建任务 xxx / 切换到任务 abc12345 或 openclaw / 把任务标题改成 xxx / 设置工作目录为 /root/CodexBot",
                    "常见命令：查看当前路径 / 列出目录 / 运行测试 / 查看文件 README.md / 搜索 worker",
                    "审批与确认：批准命令 / 拒绝命令 / 确认执行 / 取消执行",
                    "普通中文默认会作为任务备注处理。",
                ]
            )
        )

    async def help_voice(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(
            update.effective_message,
            "\n".join(
                [
                    "语音帮助：",
                    "状态查询：看看任务状态 / 查看任务列表 / 看看队列 / 看看进度",
                    "查看结果：看看最近输出 / 看看历史 / 看看备注 / 看看待审批命令",
                    "任务管理：新建任务 xxx / 切换到任务 abc12345 或 openclaw / 把任务标题改成 xxx / 设置工作目录为 /root/CodexBot",
                    "常见语音命令：查看当前路径 / 列出目录 / 运行测试 / 查看文件 README.md / 搜索 worker",
                    "语音识别成命令后，请发送：确认执行 / 取消执行",
                    "建议说短句，命令和任务说明分开发，识别会更稳定。",
                ]
            ),
        )

    async def diagnose_zh(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        text = " ".join(context.args).strip()
        if not text:
            await self._reply(update.effective_message, "用法：/diagnose_zh 你的中文句子")
            return
        canonical = self._canonicalize_chinese_text(text)
        normalized_command = self._normalize_spoken_command(canonical)
        likely_model = self._should_use_model_intent_parser(canonical)
        await self._reply(
            update.effective_message,
            "\n".join(
                [
                    f"原句：{text}",
                    f"归一化后：{canonical}",
                    f"口语命令映射：{normalized_command or '-'}",
                    f"可能触发模型意图解析：{likely_model}",
                    "如果不是固定动作或命令口语，通常会作为任务备注处理。",
                ]
            ),
        )

    async def status(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        task_ref = " ".join(context.args).strip()
        try:
            state = self.worker.get_task_state(task_ref) if task_ref else self.worker.get_state()
        except FileNotFoundError:
            await self._reply(update.effective_message, f"找不到任务：{task_ref}")
            return
        await self._reply(update.effective_message, self._format_state_details(state))

    async def tasks(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(update.effective_message, self.worker.list_tasks())

    async def new_task(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        title = " ".join(context.args).strip() or "New remote task"
        state = self.worker.create_task(title)
        await self._reply(update.effective_message, f"已创建任务。\n任务ID：{state.task_id}\n任务：{state.title}")

    async def switch_task(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        task_id = " ".join(context.args).strip()
        if not task_id:
            await self._reply(update.effective_message, "用法：/switchtask task_id")
            return
        try:
            state = self.worker.switch_task(task_id)
        except FileNotFoundError:
            await self._reply(update.effective_message, f"找不到任务：{task_id}")
            return
        await self._reply(update.effective_message, f"已切换任务。\n任务ID：{state.task_id}\n任务：{state.title}")

    async def continue_task(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        instruction = " ".join(context.args).strip() or None
        state = self.worker.continue_task(instruction)
        await self._reply(
            update.effective_message,
            "继续执行。\n"
            f"最新状态：{state.latest_update}\n"
            "可以继续发送普通中文、/note，或 /run。",
        )

    async def pause(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.pause_task()
        await self._reply(update.effective_message, f"已暂停。\n最新状态：{state.latest_update}")

    async def note(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        text = " ".join(context.args).strip()
        if not text:
            await self._reply(update.effective_message, "用法：/note 你的说明 或 /note cmd: pwd")
            return
        state = self.worker.add_note(text)
        await self._reply(update.effective_message, f"已记录备注。\n最新状态：{state.latest_update}")

    async def run_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        text = " ".join(context.args).strip()
        if not text:
            await self._reply(update.effective_message, "用法：/run 你的 shell 命令")
            return
        state = self.worker.run_command(text)
        await self._reply(update.effective_message, f"命令已入队。\n最新状态：{state.latest_update}")

    async def confirm_run(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.confirm_voice_command()
        await self._reply(update.effective_message, f"已确认执行。\n最新状态：{state.latest_update}")

    async def cancel_run(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.cancel_voice_command()
        await self._reply(update.effective_message, f"已取消。\n最新状态：{state.latest_update}")

    async def approve(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.approve()
        await self._reply(update.effective_message, f"已批准。\n最新状态：{state.latest_update}")

    async def reject(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.reject_pending_command()
        await self._reply(update.effective_message, f"已拒绝。\n最新状态：{state.latest_update}")

    async def set_workdir(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        text = " ".join(context.args).strip()
        if not text:
            await self._reply(update.effective_message, "用法：/setworkdir /absolute/path")
            return
        state = self.worker.set_workdir(text)
        await self._reply(update.effective_message, f"已更新工作目录。\n目录：{state.workdir}")

    async def set_title(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        text = " ".join(context.args).strip()
        if not text:
            await self._reply(update.effective_message, "用法：/settitle 任务标题")
            return
        state = self.worker.set_title(text)
        await self._reply(update.effective_message, f"已更新任务标题。\n任务：{state.title}")

    async def notes(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(update.effective_message, self.worker.format_recent_notes())

    async def queue(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(update.effective_message, self.worker.format_queue())

    async def progress(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(update.effective_message, self.worker.progress_summary())

    async def history(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        await self._reply(update.effective_message, self.worker.format_command_history())

    async def tail(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.get_state()
        await self._reply(update.effective_message, state.last_command_output or "还没有命令输出。")

    async def agent(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        text = " ".join(context.args).strip().lower()
        if text not in {"on", "off"}:
            await self._reply(update.effective_message, "用法：/agent on|off")
            return
        state = self.worker.set_agent_enabled(text == "on")
        await self._reply(update.effective_message, f"已更新 Agent 状态。\n启用：{state.agent_enabled}")

    async def reset(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        state = self.worker.reset_state()
        await self._reply(update.effective_message, f"已重置状态。\n工作目录：{state.workdir}")

    async def handle_text(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        message = update.effective_message
        if message is None:
            return
        text = (message.text or "").strip()
        if not text:
            await self._reply(message, "请发送任务说明，或使用 /help_zh、/help_voice、/status、/run 这类命令。")
            return
        if text.startswith("/") and len(text) > 1:
            slash_body = text[1:].strip()
            if self._looks_like_chinese_phrase(slash_body):
                await self._reply(
                    message,
                    "你发的是中文短语，不需要加 `/`。\n"
                    f"请直接发送：{slash_body}\n"
                    "如果你想用斜杠命令，请使用 /help_zh 查看可用命令。"
                )
                return
        await self._handle_instruction_text(message, text, source="text")

    async def handle_voice(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
        if not await self.ensure_allowed(update):
            return
        message = update.effective_message
        voice = message.voice if message is not None else None
        if message is None or voice is None:
            return

        await self._reply(message, "正在识别语音，请稍等。")
        try:
            telegram_file = await voice.get_file()
            buffer = BytesIO()
            await telegram_file.download_to_memory(buffer)
            buffer.seek(0)
            text = self.transcriber.transcribe(buffer.read(), filename="voice.ogg")
        except Exception as exc:
            await self._reply(message, f"语音识别失败：{exc}")
            return

        await self._reply(message, f"识别结果：{text}")
        await self._handle_instruction_text(message, text, source="voice")

    async def _handle_instruction_text(self, message, text: str, source: str) -> None:
        normalized = text.strip()
        canonical = self._canonicalize_chinese_text(normalized)
        lowered = canonical.lower()
        model_handled = await self._try_handle_model_intent_plan(message, normalized, canonical, lowered)
        if model_handled:
            return
        composite_handled = await self._try_handle_composite_request(message, normalized, canonical, lowered)
        if composite_handled:
            return
        action_handled = await self._try_handle_natural_action(message, normalized, canonical, lowered)
        if action_handled:
            return

        normalized_command = self._normalize_spoken_command(canonical)
        if normalized_command:
            if source == "voice":
                state = self.worker.stage_voice_command(normalized_command)
                await self._reply(
                    message,
                    "识别到口语命令，等待确认。\n"
                    f"命令：{state.pending_voice_command}\n"
                    "发送 /confirmrun 执行，或 /cancelrun 取消。"
                )
                return
            state = self.worker.run_command(normalized_command)
            await self._reply(message, f"已按命令处理。\n最新状态：{state.latest_update}")
            return

        if lowered.startswith("cmd:"):
            if source == "voice":
                state = self.worker.stage_voice_command(canonical[4:].strip())
                await self._reply(
                    message,
                    "识别到命令，等待确认。\n"
                    f"命令：{state.pending_voice_command}\n"
                    "发送 /confirmrun 执行，或 /cancelrun 取消。"
                )
                return
            state = self.worker.run_command(canonical[4:].strip())
            await self._reply(message, f"已按命令处理。\n最新状态：{state.latest_update}")
            return

        run_prefixes = ("运行 ", "執行 ", "执行 ", "run ")
        for prefix in run_prefixes:
            if lowered.startswith(prefix):
                command = canonical[len(prefix):].strip()
                if command:
                    if source == "voice":
                        state = self.worker.stage_voice_command(command)
                        await self._reply(
                            message,
                            "识别到命令，等待确认。\n"
                            f"命令：{state.pending_voice_command}\n"
                            "发送 /confirmrun 执行，或 /cancelrun 取消。"
                        )
                        return
                    state = self.worker.run_command(command)
                    await self._reply(message, f"已按命令处理。\n最新状态：{state.latest_update}")
                    return

        state = self.worker.add_note(normalized)
        await self._reply(
            message,
            f"已作为任务备注记录（来源：{source}）。\n"
            f"最新状态：{state.latest_update}"
        )

    def _normalize_spoken_command(self, text: str) -> str | None:
        normalized = text.strip()
        lowered = normalized.lower()

        exact_map = {
            "查看当前路径": "pwd",
            "显示当前路径": "pwd",
            "当前路径": "pwd",
            "查看目录": "ls -la",
            "列出目录": "ls -la",
            "看看目录": "ls -la",
            "查看文件": "ls -la",
            "运行测试": "pytest",
            "跑测试": "pytest",
            "执行测试": "pytest",
        }
        if normalized in exact_map:
            return exact_map[normalized]

        prefix_map = {
            "查看文件 ": "cat ",
            "打开文件 ": "cat ",
            "搜索 ": "rg ",
            "查找 ": "rg ",
            "列出 ": "ls ",
            "进入目录 ": "cd ",
        }
        for prefix, command_prefix in prefix_map.items():
            if normalized.startswith(prefix):
                remainder = normalized[len(prefix):].strip()
                if remainder:
                    return f"{command_prefix}{remainder}"

        if lowered in {"pwd", "ls", "ls -la", "pytest"}:
            return lowered

        return None

    async def _try_handle_natural_action(self, message, original: str, normalized: str, lowered: str) -> bool:
        if normalized in {"看看任务状态", "查看任务状态", "任务状态", "当前状态", "看看状态"}:
            state = self.worker.get_state()
            await self._reply(message, self._format_state_snapshot(state))
            return True

        status_prefixes = ("查看 ", "看看 ")
        status_suffixes = (" 的状态", " 狀態", " 的狀態", " 状态")
        for prefix in status_prefixes:
            if normalized.startswith(prefix):
                remainder = normalized[len(prefix):].strip()
                for suffix in status_suffixes:
                    if remainder.endswith(suffix):
                        task_ref = remainder[: -len(suffix)].strip()
                        if task_ref:
                            try:
                                state = self.worker.get_task_state(task_ref)
                            except FileNotFoundError:
                                await self._reply(message, f"找不到任务：{task_ref}")
                                return True
                            await self._reply(message, self._format_state_details(state))
                            return True

        if normalized in {"看看任务列表", "查看任务列表", "任务列表", "所有任务"}:
            await self._reply(message, self.worker.list_tasks())
            return True

        if normalized in {"看看队列", "查看队列", "任务队列", "当前队列"}:
            await self._reply(message, self.worker.format_queue())
            return True

        if normalized in {"看看进度", "查看进度", "当前进度", "任务进度"}:
            await self._reply(message, self.worker.progress_summary())
            return True

        if normalized in {"看看最近输出", "查看最近输出", "最近输出", "最近命令输出"}:
            state = self.worker.get_state()
            await self._reply(message, state.last_command_output or "还没有命令输出。")
            return True

        if normalized in {"看看历史", "查看历史", "最近历史", "命令历史"}:
            await self._reply(message, self.worker.format_command_history())
            return True

        if normalized in {"看看备注", "查看备注", "最近备注"}:
            await self._reply(message, self.worker.format_recent_notes())
            return True

        if normalized in {"看看待审批命令", "查看待审批命令", "待审批命令"}:
            state = self.worker.get_state()
            await self._reply(
                message,
                "\n".join(
                    [
                        f"待审批命令：{state.pending_approval_command or '-'}",
                        f"审批原因：{state.pending_approval_reason or '-'}",
                        f"待确认语音命令：{state.pending_voice_command or '-'}",
                    ]
                )
            )
            return True

        if normalized in {"确认执行", "确认运行"}:
            state = self.worker.confirm_voice_command()
            await self._reply(message, f"已确认执行。\n最新状态：{state.latest_update}")
            return True

        if normalized in {"批准命令", "批准执行", "同意执行"}:
            state = self.worker.approve()
            await self._reply(message, f"已批准。\n最新状态：{state.latest_update}")
            return True

        if normalized in {"取消执行", "取消运行"}:
            state = self.worker.cancel_voice_command()
            await self._reply(message, f"已取消。\n最新状态：{state.latest_update}")
            return True

        if normalized in {"拒绝命令", "拒绝执行"}:
            state = self.worker.reject_pending_command()
            await self._reply(message, f"已拒绝。\n最新状态：{state.latest_update}")
            return True

        new_task_prefixes = ("新建任务 ", "创建任务 ", "新任务 ")
        for prefix in new_task_prefixes:
            if normalized.startswith(prefix):
                title = normalized[len(prefix):].strip()
                if title:
                    state = self.worker.create_task(title)
                    await self._reply(message, f"已创建任务。\n任务ID：{state.task_id}\n任务：{state.title}")
                    return True

        switch_prefixes = ("切换到任务 ", "切换任务 ", "进入任务 ")
        for prefix in switch_prefixes:
            if normalized.startswith(prefix):
                task_id = normalized[len(prefix):].strip()
                if task_id:
                    try:
                        state = self.worker.switch_task(task_id)
                    except FileNotFoundError:
                        await self._reply(message, f"找不到任务：{task_id}")
                        return True
                    await self._reply(message, f"已切换任务。\n任务ID：{state.task_id}\n任务：{state.title}")
                    return True

        title_prefixes = ("把任务标题改成 ", "修改任务标题为 ", "设置任务标题为 ")
        for prefix in title_prefixes:
            if normalized.startswith(prefix):
                title = normalized[len(prefix):].strip()
                if title:
                    state = self.worker.set_title(title)
                    await self._reply(message, f"已更新任务标题。\n任务：{state.title}")
                    return True

        workdir_prefixes = ("设置工作目录为 ", "把工作目录设为 ", "工作目录设为 ")
        for prefix in workdir_prefixes:
            if normalized.startswith(prefix):
                workdir = normalized[len(prefix):].strip()
                if workdir:
                    state = self.worker.set_workdir(workdir)
                    await self._reply(message, f"已更新工作目录。\n目录：{state.workdir}")
                    return True

        if lowered in {"暂停任务", "暂停"}:
            state = self.worker.pause_task()
            await self._reply(message, f"已暂停。\n最新状态：{state.latest_update}")
            return True

        if lowered in {"继续任务", "继续"}:
            state = self.worker.continue_task()
            await self._reply(message, f"继续执行。\n最新状态：{state.latest_update}")
            return True

        return False

    async def _try_handle_model_intent_plan(self, message, original: str, normalized: str, lowered: str) -> bool:
        if not self._should_use_model_intent_parser(normalized):
            return False
        try:
            plan = self.intent_parser.parse(self.worker.get_state(), normalized)
        except Exception:
            return False
        if not plan or not plan.steps:
            return False

        summaries: list[str] = [f"我把你的请求拆成了 {len(plan.steps)} 步："]
        for index, step in enumerate(plan.steps, start=1):
            line = await self._execute_intent_step(message, step.action, step.text)
            summaries.append(f"{index}. {line}")
        summaries.append(f"摘要：{plan.summary}")
        await self._reply(message, "\n".join(summaries))
        return True

    async def _execute_intent_step(self, message, action: str, text: str | None) -> str:
        if action == "status":
            state = self.worker.get_state()
            await self._reply(message, self._format_state_snapshot(state))
            return "查看了当前任务状态"

        if action == "tasks":
            await self._reply(message, self.worker.list_tasks())
            return "查看了任务列表"

        if action == "queue":
            await self._reply(message, self.worker.format_queue())
            return "查看了当前队列"

        if action == "progress":
            await self._reply(message, self.worker.progress_summary())
            return "查看了当前进度"

        if action == "history":
            await self._reply(message, self.worker.format_command_history())
            return "查看了最近历史"

        if action == "tail":
            state = self.worker.get_state()
            await self._reply(message, state.last_command_output or "还没有命令输出。")
            return "查看了最近输出"

        if action == "pending":
            state = self.worker.get_state()
            await self._reply(
                message,
                f"待审批命令：{state.pending_approval_command or '-'}\n审批原因：{state.pending_approval_reason or '-'}\n待确认语音命令：{state.pending_voice_command or '-'}",
            )
            return "查看了待审批信息"

        if action == "approve":
            state = self.worker.approve()
            await self._reply(message, f"已批准。\n最新状态：{state.latest_update}")
            return "批准了待执行命令"

        if action == "reject":
            state = self.worker.reject_pending_command()
            await self._reply(message, f"已拒绝。\n最新状态：{state.latest_update}")
            return "拒绝了待执行命令"

        if action == "pause":
            state = self.worker.pause_task()
            await self._reply(message, f"已暂停。\n最新状态：{state.latest_update}")
            return "暂停了当前任务"

        if action == "continue":
            state = self.worker.continue_task()
            await self._reply(message, f"继续执行。\n最新状态：{state.latest_update}")
            return "继续了当前任务"

        if action == "create_task" and text:
            state = self.worker.create_task(text)
            await self._reply(message, f"已创建任务。\n任务ID：{state.task_id}\n任务：{state.title}")
            return f"创建了任务：{text}"

        if action == "switch_task" and text:
            try:
                state = self.worker.switch_task(text)
            except FileNotFoundError:
                await self._reply(message, f"找不到任务：{text}")
                return f"尝试切换任务失败：{text}"
            await self._reply(message, f"已切换任务。\n任务ID：{state.task_id}\n任务：{state.title}")
            return f"切换到任务：{text}"

        if action == "set_title" and text:
            state = self.worker.set_title(text)
            await self._reply(message, f"已更新任务标题。\n任务：{state.title}")
            return f"更新了任务标题：{text}"

        if action == "set_workdir" and text:
            state = self.worker.set_workdir(text)
            await self._reply(message, f"已更新工作目录。\n目录：{state.workdir}")
            return f"更新了工作目录：{text}"

        if action == "note" and text:
            state = self.worker.add_note(text)
            await self._reply(message, f"已记录备注。\n最新状态：{state.latest_update}")
            return f"记录了备注：{text}"

        state = self.worker.add_note(text or action)
        await self._reply(message, f"已记录备注。\n最新状态：{state.latest_update}")
        return f"记录了保守备注：{text or action}"

    async def _try_handle_composite_request(self, message, original: str, normalized: str, lowered: str) -> bool:
        patterns = [
            ("看看最近输出，如果", "看看最近输出"),
            ("查看最近输出，如果", "看看最近输出"),
            ("看看任务状态，如果", "看看任务状态"),
            ("查看任务状态，如果", "看看任务状态"),
            ("看看进度，如果", "看看进度"),
            ("查看进度，如果", "看看进度"),
        ]

        for marker, primary_action in patterns:
            if normalized.startswith(marker):
                follow_up = normalized.split("，如果", 1)[1].strip() if "，如果" in normalized else normalized.split("如果", 1)[1].strip()
                await self._try_handle_natural_action(message, primary_action, primary_action, primary_action.lower())
                state = self.worker.add_note(f"后续要求：如果{follow_up}")
                await self._reply(
                    message,
                    "我已把复合请求拆成两步处理：\n"
                    f"1. 先执行：{primary_action}\n"
                    f"2. 再记录后续要求：如果{follow_up}\n"
                    f"最新状态：{state.latest_update}"
                )
                return True

        sequential_patterns = [
            ("先看看最近输出，然后", "看看最近输出"),
            ("先查看最近输出，然后", "看看最近输出"),
            ("先看看任务状态，然后", "看看任务状态"),
            ("先查看任务状态，然后", "看看任务状态"),
            ("先看看进度，然后", "看看进度"),
            ("先查看进度，然后", "看看进度"),
        ]

        for marker, primary_action in sequential_patterns:
            if normalized.startswith(marker):
                follow_up = normalized.split("然后", 1)[1].strip()
                await self._try_handle_natural_action(message, primary_action, primary_action, primary_action.lower())
                state = self.worker.add_note(f"后续要求：{follow_up}")
                await self._reply(
                    message,
                    "我已把复合请求拆成两步处理：\n"
                    f"1. 先执行：{primary_action}\n"
                    f"2. 再记录后续要求：{follow_up}\n"
                    f"最新状态：{state.latest_update}"
                )
                return True

        return False

    def _format_state_snapshot(self, state) -> str:
        return "\n".join(
            [
                f"任务：{state.title}",
                f"任务ID：{state.task_id}",
                f"状态：{state.status.value}",
                f"最新：{state.latest_update}",
            ]
        )

    def _format_state_details(self, state) -> str:
        pending_items = sum(1 for note in state.notes if note.processed_at is None)
        return "\n".join(
            [
                f"Task: {state.title}",
                f"Task ID: {state.task_id}",
                f"Status: {state.status.value}",
                f"Summary: {state.summary}",
                f"Latest: {state.latest_update}",
                f"Workdir: {state.workdir or '-'}",
                f"Pending items: {pending_items}",
                f"Completed steps: {state.completed_steps}",
                f"Pending approval: {state.pending_approval_command or '-'}",
                f"Approval reason: {state.pending_approval_reason or '-'}",
                f"Pending voice command: {state.pending_voice_command or '-'}",
                f"Last command: {state.last_command or '-'}",
                f"Agent enabled: {state.agent_enabled}",
                f"Planner summary: {state.planner_last_summary or '-'}",
                f"Last output: {state.last_command_output or '-'}",
            ]
        )

    def _canonicalize_chinese_text(self, text: str) -> str:
        normalized = text.strip()
        replacements = {
            "任務": "任务",
            "狀態": "状态",
            "當前": "当前",
            "當前狀態": "当前状态",
            "當前路徑": "当前路径",
            "路徑": "路径",
            "執行": "执行",
            "歷史": "历史",
            "輸出": "输出",
            "備註": "备注",
            "佇列": "队列",
            "進度": "进度",
            "檢查": "检查",
            "總結": "总结",
            "設置": "设置",
            "設為": "设为",
            "標題": "标题",
            "切換": "切换",
            "創建": "创建",
            "新建任務": "新建任务",
            "創建任務": "创建任务",
            "批准命令": "批准命令",
            "拒絕命令": "拒绝命令",
            "確認執行": "确认执行",
            "取消執行": "取消执行",
            "查看任務列表": "查看任务列表",
            "看看任務狀態": "看看任务状态",
            "查看任務狀態": "查看任务状态",
            "看看進度": "看看进度",
            "查看進度": "查看进度",
            "看看最近輸出": "看看最近输出",
            "查看最近輸出": "查看最近输出",
            "看看備註": "看看备注",
            "查看備註": "查看备注",
            "看看待審批命令": "看看待审批命令",
            "查看待審批命令": "查看待审批命令",
            "設定工作目錄為": "设置工作目录为",
            "設置工作目錄為": "设置工作目录为",
            "把任務標題改成": "把任务标题改成",
            "切換到任務": "切换到任务",
        }
        for src, dst in replacements.items():
            normalized = normalized.replace(src, dst)
        return normalized

    def _looks_like_chinese_phrase(self, text: str) -> bool:
        return any("\u4e00" <= char <= "\u9fff" for char in text)

    def _should_use_model_intent_parser(self, text: str) -> bool:
        triggers = ("如果", "然后", "並且", "并且", "順便", "顺便", "同时", "同時")
        return len(text) >= 18 and any(trigger in text for trigger in triggers)

    async def _reply(self, message, text: str) -> None:
        if message is None:
            return
        source_text = (message.text or message.caption or "").strip()
        await message.reply_text(self._localize_reply(text, source_text))

    def _localize_reply(self, text: str, source_text: str) -> str:
        if not self._prefers_traditional(source_text):
            return text
        replacements = {
            "上线": "上線",
            "可用命令": "可用命令",
            "发送": "發送",
            "语音": "語音",
            "状态": "狀態",
            "任务": "任務",
            "备注": "備註",
            "历史": "歷史",
            "输出": "輸出",
            "队列": "佇列",
            "进度": "進度",
            "当前": "當前",
            "路径": "路徑",
            "确认": "確認",
            "执行": "執行",
            "取消": "取消",
            "拒绝": "拒絕",
            "批准": "批准",
            "目录": "目錄",
            "标题": "標題",
            "创建": "創建",
            "切换": "切換",
            "工作目录": "工作目錄",
            "已记录": "已記錄",
            "已更新": "已更新",
            "识别": "識別",
            "帮助": "幫助",
            "简短": "簡短",
            "结果": "結果",
            "还有": "還有",
        }
        localized = text
        for src, dst in replacements.items():
            localized = localized.replace(src, dst)
        return localized

    def _prefers_traditional(self, source_text: str) -> bool:
        traditional_markers = "任務狀態當前路徑執行歷史輸出備註進度檢查總結設置標題切換創建"
        return any(char in source_text for char in traditional_markers)

    def run(self) -> None:
        self.application.run_polling(allowed_updates=Update.ALL_TYPES)
