<?php

namespace XZD\AI\V5\REST\Controllers;

use XZD\AI\V5\REST\BaseController;
use XZD\AI\V5\Agents\SessionManager;
use XZD\AI\V5\Agents\OrchestratorAgent;
use XZD\AI\V5\Agents\ActionDispatcher;
use XZD\AI\V5\Agents\AgentLoop;
use XZD\AI\V5\Agents\ContextBuilder;
use XZD\AI\V5\Agents\MonitorAgent;
use XZD\AI\V5\AI\RateLimiter;
use XZD\AI\V5\AI\StreamEngine;
use XZD\AI\V5\Core\Logger;
use XZD\AI\V5\Core\UserHelper;
use XZD\AI\V5\Tools\ToolRegistry;
use WP_REST_Request;
use WP_REST_Response;

/**
 * 对话系统控制器 — AI 对话相关 REST 端点
 */
class ChatMessageController extends BaseController
{
    private OrchestratorAgent $orchestrator;

    public function __construct()
    {
        $this->orchestrator = new OrchestratorAgent();
    }

    public function registerRoutes(): void
    {
        // 会话管理（游客可访问）
        $this->registerGetPublic('/chat/sessions', [$this, 'listSessions']);
        $this->registerPostPublic('/chat/sessions', [$this, 'createSession']);
        $this->registerGetPublic('/chat/sessions/(?P<key>[a-zA-Z0-9_]+)', [$this, 'getSession']);
        $this->registerDelete('/chat/sessions/(?P<key>[a-zA-Z0-9_]+)', [$this, 'deleteSession'], 'read');

        // 对话（游客可访问，具体权限由 ToolRegistry 控制）
        $this->registerPostPublic('/chat/send', [$this, 'send']);
        $this->registerGetPublic('/chat/stream', [$this, 'stream']);
        $this->registerGetPublic('/chat/stream/(?P<stream_id>[a-zA-Z0-9_]+)/reconnect', [$this, 'reconnect']);

        // 对话操作（游客可访问）
        $this->registerGetPublic('/chat/quota', [$this, 'quota']);
        $this->registerGetPublic('/chat/actions', [$this, 'availableActions']);
        $this->registerPostPublic('/chat/execute', [$this, 'execute']);
        $this->registerPost('/chat/rate', [$this, 'rateMessage'], 'read');
        $this->registerPost('/chat/stop', [$this, 'stopStream'], 'read');

        // 主动式 AI
        $this->registerPostPublic('/chat/proactive-trigger', [$this, 'proactiveTrigger']);
        $this->registerGetPublic('/chat/proactive-suggestions', [$this, 'proactiveSuggestions']);
    }

    // —— 访客令牌 ——

    /**
     * 获取或生成访客令牌
     * 登录用户返回空字符串（用 user_id 隔离）
     */
    private function resolveGuestToken(WP_REST_Request $request): string
    {
        $userId = get_current_user_id();
        if ($userId > 0) {
            return '';
        }
        $token = $this->getParam($request, 'guest_token', '');
        if (!empty($token) && preg_match('/^[a-f0-9\-]{36}$/', $token)) {
            return $token;
        }
        return SessionManager::generateGuestToken();
    }

    // 会话管理

    public function listSessions(WP_REST_Request $request): WP_REST_Response
    {
        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);
        $limit      = $this->getIntParam($request, 'limit', 20);
        $offset     = $this->getIntParam($request, 'offset', 0);

        $sm = new SessionManager();
        $sessions = $sm->listSessions($userId, $guestToken, min($limit, 50), $offset);

        return $this->success([
            'sessions'     => $sessions,
            'total'        => count($sessions),
            'guest_token'  => $userId === 0 ? $guestToken : null,
        ]);
    }

    public function createSession(WP_REST_Request $request): WP_REST_Response
    {
        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);
        $title      = $this->getParam($request, 'title', '新对话');

        $sm = new SessionManager();
        $result = $sm->createSession($userId, $guestToken, $title);

        if ($result['success']) {
            if ($userId === 0) {
                $result['guest_token'] = $guestToken;
            }
            return $this->created($result);
        }
        return $this->error($result['message'] ?? '创建失败', 'session_create_failed');
    }

    public function getSession(WP_REST_Request $request): WP_REST_Response
    {
        $sessionKey = $request->get_param('key');
        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);

        $sm = new SessionManager();
        $session = $sm->getSession($sessionKey, $userId, $guestToken);

        if (!$session) {
            return $this->error('会话不存在或无权访问', 'session_not_found', 404);
        }

        $messages = $sm->getMessages($sessionKey, 100, $userId, $guestToken);

        return $this->success([
            'session'  => $session,
            'messages' => $messages,
        ]);
    }

    public function deleteSession(WP_REST_Request $request): WP_REST_Response
    {
        $sessionKey = $request->get_param('key');
        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);
        $sm = new SessionManager();

        $ok = $sm->deleteSession($sessionKey, $userId, $guestToken);
        if (!$ok) {
            return $this->error('会话不存在或无权删除', 'session_not_found', 404);
        }
        return $this->noContent('会话已删除');
    }

    // 对话核心

    public function send(WP_REST_Request $request): WP_REST_Response
    {
        $message     = $this->getParam($request, 'message', '');
        $sessionKey  = $this->getParam($request, 'session_key', '');
        $confirmKey   = $this->getParam($request, 'confirm_key', '');
        $repeatChoice = $this->getParam($request, 'repeat_choice', '');
        $model        = $this->getParam($request, 'model', '');

        if (empty(trim($message)) && empty($confirmKey)) {
            return $this->error('消息不能为空', 'empty_message');
        }

        // 免费版可使用AI对话
        if ($err = $this->guardLicense('free')) return $err;

        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);
        $role       = $this->getUserRole($userId);

        // 速率检查
        $limiter = RateLimiter::forCurrentUser();
        $check = $limiter->check();
        if (!$check['allowed']) {
            return $this->error($check['message'], 'rate_limited', 429);
        }

        // 处理确认回复（含重复处理多选项）
        if (!empty($confirmKey)) {
            return $this->handleConfirm($confirmKey, $sessionKey, $userId, $guestToken, $repeatChoice);
        }

        // 自动创建会话
        if (empty($sessionKey)) {
            $sm = new SessionManager();
            $result = $sm->createSession($userId, $guestToken, mb_substr($message, 0, 50));
            if ($result['success']) {
                $sessionKey = $result['session_key'];
            }
        }

        try {
            $options = [];
            if ($model) {
                $options['model'] = $model;
            }

            $result = $this->orchestrator->handleWithTools(
                $sessionKey, $message, $role, $userId, $guestToken
            );

            $limiter->record();

            // 写入操作日志
            Logger::operation([
                'tool_id'     => 'chat_message',
                'tool_name'   => 'AI对话',
                'category'    => 'chat',
                'target_type' => 'session',
                'target_id'   => $sessionKey,
                'params'      => ['message' => mb_substr($message, 0, 200)],
                'result'      => ['content' => mb_substr($result['content'] ?? '', 0, 500)],
                'status'      => 'success',
                'user_id'     => $userId,
                'session_key' => $sessionKey,
                'cost_tokens'  => ($result['input_tokens'] ?? 0) + ($result['output_tokens'] ?? 0),
                'duration_ms'  => $result['duration_ms'] ?? 0,
            ]);

            // 服务端生成角色化追问
            $followups = $this->generateFollowups($role, $result['content'] ?? '');

            $resp = [
                'session_key'     => $sessionKey,
                'content'         => $result['content'] ?? '',
                'tool_calls'      => $result['tool_calls'] ?? null,
                'confirm_required'=> $result['confirm_required'] ?? false,
                'confirm_key'     => $result['confirm_key'] ?? '',
                'confirm_detail'  => $result['confirm_detail'] ?? '',
                'followups'       => $followups,
                'turns'           => $result['turns'] ?? 1,
                'remain'          => $limiter->getRemaining(),
            ];
            if ($userId === 0) {
                $resp['guest_token'] = $guestToken;
            }
            return $this->success($resp);
        } catch (\Throwable $e) {
            Logger::error('chat_send', $e->getMessage());
            return $this->error('对话处理失败: ' . $e->getMessage(), 'chat_error', 500);
        }
    }

    /**
     * SSE 流式对话 + 工具执行（ReAct 多轮循环）
     */
    public function stream(WP_REST_Request $request): void
    {
        $message    = $this->getParam($request, 'message', '');
        $sessionKey = $this->getParam($request, 'session_key', '');
        $streamId   = $this->getParam($request, 'stream_id', '');
        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);
        $role       = $this->getUserRole($userId);

        if (empty(trim($message))) {
            $this->emitSSE(['error' => '消息不能为空'], 'error');
            $this->emitSSE(['done' => true], 'done');
            return;
        }

        $limiter = RateLimiter::forCurrentUser();
        if (!$limiter->check()['allowed']) {
            $this->emitSSE(['error' => '请求过于频繁'], 'error');
            $this->emitSSE(['done' => true], 'done');
            return;
        }

        // 流式调用 — 先检查 provider 再创建副作用
        $provider = $this->selectStreamProvider();
        if (!$provider) {
            wp_send_json_error(['message' => '没有可用的 AI 连接，请前往「小栈AI → AI配置 → 文本连接池」添加 API 连接'], 503);
            return;
        }

        if (empty($streamId)) {
            $streamId = 'strm_' . bin2hex(random_bytes(12));
        }

        $this->setSSEHeaders($streamId);

        // 会话
        $sm = new SessionManager();
        if (empty($sessionKey)) {
            $r = $sm->createSession($userId, $guestToken, mb_substr($message, 0, 50));
            if ($r['success']) $sessionKey = $r['session_key'];
        }

        // 构建消息（per-user + config 上下文拼到 user 消息开头，保持 system 前缀稳定命中缓存）
        $cb = new ContextBuilder();
        $systemPrompt = $cb->buildSystemPromptWithUser($userId, $role);
        $userPrefix   = $cb->getConfigContextPrefix($userId) . $cb->getUserContextPrefix($userId, $role);
        $history = $sm->getRecentMessages($sessionKey, 20, $userId, $guestToken);
        $messages = array_merge(
            [['role' => 'system', 'content' => $systemPrompt]],
            $history,
            [['role' => 'user', 'content' => $userPrefix . $message]]
        );
        $sm->addMessage($sessionKey, 'user', $message, [], $userId, $guestToken);

        // 获取工具列表
        $tools = ToolRegistry::instance()->toOpenAIFunctions($role);
        $options = ['disable_thinking' => true];
        if (!empty($tools)) {
            $options['tools'] = $tools;
            $options['tool_choice'] = 'auto';
        }

        $fullContent = '';
        $allValidCalls = [];
        $allToolResults = [];
        $maxTurns = 5;

        // ReAct 多轮循环：AI 回复 → 工具执行 → 结果反馈 → AI 再回复
        for ($turn = 0; $turn < $maxTurns; $turn++) {
            $turnToolCalls = [];
            $turnContent = '';

            // 真流式：用 onChunk 回调在 chunk 到达时立即发送 SSE
            $generator = $provider->chatStream($messages, $options, function ($chunk) use (&$turnContent, &$turnToolCalls) {
                if (!empty($chunk['error'])) {
                    $this->emitSSE(['error' => $chunk['error']], 'error');
                    return;
                }
                $delta = $chunk['delta'] ?? '';
                if (!empty($delta)) {
                    $turnContent .= $delta;
                    $this->emitSSE(['delta' => $delta], 'text');
                }
                if (!empty($chunk['tool_call'])) {
                    $incoming = $chunk['tool_call'];
                    foreach ($incoming as $tc) {
                        $idx = $tc['index'] ?? 0;
                        if (!isset($turnToolCalls[$idx])) {
                            $turnToolCalls[$idx] = $tc;
                        } else {
                            if (!empty($tc['id'])) $turnToolCalls[$idx]['id'] = $tc['id'];
                            if (!empty($tc['type'])) $turnToolCalls[$idx]['type'] = $tc['type'];
                            if (!empty($tc['function']['name'])) $turnToolCalls[$idx]['function']['name'] = $tc['function']['name'];
                            if (isset($tc['function']['arguments'])) $turnToolCalls[$idx]['function']['arguments'] .= $tc['function']['arguments'];
                        }
                    }
                    $this->emitSSE(['tool_calls' => $turnToolCalls], 'tool_call');
                }
                if ($chunk['done'] ?? false) return;
            });

            foreach ($generator as $_) {}

            $fullContent .= $turnContent;

            // 过滤空函数名的 tool_call
            $turnToolCalls = array_values(array_filter($turnToolCalls, function ($tc) {
                return !empty($tc['function']['name'] ?? '');
            }));

            // 无工具调用 → AI 本轮直接回复，循环结束
            if (empty($turnToolCalls)) {
                break;
            }

            // 助手回复（含 tool_calls）加入消息历史
            $messages[] = [
                'role'       => 'assistant',
                'content'    => $turnContent,
                'tool_calls' => $turnToolCalls,
            ];

            // 执行本轮工具调用
            $dispatcher = ActionDispatcher::instance();
            $turnValidCalls = [];
            $turnToolResults = [];
            $hasConfirm = false;

            foreach ($turnToolCalls as $tc) {
                $funcName = $tc['function']['name'] ?? '';
                if (empty($funcName)) continue;
                $toolMeta = \XZD\AI\V5\Tools\ToolRegistry::instance()->get($funcName);
                $displayName = $toolMeta ? ($toolMeta->name ?? $funcName) : $funcName;
                $this->emitSSE(['tool_name' => $displayName, 'tool_internal_name' => $funcName, 'tool_call_id' => $tc['id'] ?? ''], 'tool_exec');
                $args = json_decode($tc['function']['arguments'] ?? '{}', true) ?: [];
                $result = $dispatcher->dispatch($funcName, $args, $userId, $sessionKey);

                $needsDangerConfirm = ($result['confirm_required'] ?? false) && !empty($result['confirm_key']);
                $isRepeat = ($result['data']['repeat_detected'] ?? false) && !empty($result['data']['choices']);
                $isSourceSelect = ($result['data']['source_selection'] ?? false) && !empty($result['data']['choices']);

                if ($needsDangerConfirm && !$isRepeat && !$isSourceSelect) {
                    $hasConfirm = true;
                    // 保存工具调用上下文，供确认后 AI 继续多步骤任务
                    set_transient('xzd_confirm_ctx_' . $result['confirm_key'], [
                        'tool_call'  => $tc,
                        'ai_content' => $turnContent,
                    ], 300);
                    $this->emitSSE([
                        'delta'             => $result['confirm_detail'] ?? $result['confirm_message'] ?? '',
                        'type'              => 'confirm_required',
                        'confirm_key'       => $result['confirm_key'],
                        'confirm_message'   => $result['confirm_message'] ?? '',
                        'confirm_detail'    => $result['confirm_detail'] ?? '',
                        'confirm_timeout'   => $result['confirm_timeout'] ?? 300,
                        'tool_name'         => $funcName,
                        'tool_call_id'      => $tc['id'] ?? '',
                        'tool_id'           => $result['tool_id'] ?? $funcName,
                    ], 'confirm_required');
                } elseif ($isRepeat || $isSourceSelect) {
                    $hasConfirm = true;
                    $confirmData = $result['data'];
                    $prefix = $isRepeat ? 'rp_' : 'src_';
                    $transientKey = $isRepeat ? 'xzd_repeat_' : 'xzd_source_';
                    $confirmKey = $prefix . bin2hex(random_bytes(12));
                    set_transient($transientKey . $confirmKey, [
                        'tool_id'     => $funcName,
                        'params'      => $args,
                        'user_id'     => $userId,
                        'session_key' => $sessionKey,
                        'created_at'  => time(),
                    ], 300);
                    $this->emitSSE([
                        'delta'             => $result['message'] ?? ($isRepeat ? "文章已处理过「{$confirmData['tool_name']}」" : '请选择图片来源'),
                        'type'              => 'confirm_required',
                        'confirm_key'       => $confirmKey,
                        'confirm_message'   => $result['message'] ?? '',
                        'confirm_choices'   => $confirmData['choices'],
                        'confirm_timeout'   => 300,
                        'tool_name'         => $funcName,
                        'tool_call_id'      => $tc['id'] ?? '',
                    ], 'confirm_required');
                } else {
                    $this->emitSSE([
                        'delta' => json_encode($result, JSON_UNESCAPED_UNICODE),
                        'tool_name' => $funcName,
                        'tool_call_id' => $tc['id'] ?? '',
                        'tool_success' => $result['success'] ?? false,
                    ], 'tool_result');
                    $turnValidCalls[] = $tc;
                    $turnToolResults[] = ['id' => $tc['id'] ?? '', 'name' => $funcName, 'result' => $result];
                }
            }

            // 工具结果加入消息历史，供下一轮 AI 理解
            foreach ($turnToolResults as $tr) {
                $messages[] = [
                    'role'         => 'tool',
                    'tool_call_id' => $tr['id'],
                    'content'      => json_encode($tr['result'], JSON_UNESCAPED_UNICODE),
                ];
            }

            $allValidCalls = array_merge($allValidCalls, $turnValidCalls);
            $allToolResults = array_merge($allToolResults, $turnToolResults);

            // 防上下文溢出：保留 system prompt + 最多 30 条最新消息
            if (count($messages) > 31) {
                $system = $messages[0];
                $recent = array_slice($messages, -30);
                $messages = array_merge([$system], $recent);
            }

            // 需要确认 → 退出循环等待用户
            if ($hasConfirm) {
                break;
            }
        }

        $sm->addMessage($sessionKey, 'assistant', $fullContent ?: '', [
            'tool_calls' => $allValidCalls ?: null,
        ], $userId, $guestToken);

        foreach ($allToolResults as $tr) {
            $sm->addMessage($sessionKey, 'tool', json_encode($tr['result'], JSON_UNESCAPED_UNICODE), [
                'tool_call_id' => $tr['id'],
            ], $userId, $guestToken);
        }
        $sm->touchSession($sessionKey, ['title' => mb_substr($message, 0, 50)], $userId, $guestToken);

        $limiter->record();

        // 服务端生成角色化追问建议
        $followups = $this->generateFollowups($role, $fullContent);

        $donePayload = [
            'session_key' => $sessionKey,
            'stream_id'   => $streamId,
            'content'     => $fullContent,
            'followups'   => $followups,
            'done'        => true,
        ];
        if ($userId === 0) {
            $donePayload['guest_token'] = $guestToken;
        }
        $this->emitSSE($donePayload, 'done');

        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
        exit;
    }

    /**
     * SSE 断线重连
     */
    public function reconnect(WP_REST_Request $request): WP_REST_Response
    {
        $streamId = $request->get_param('stream_id');
        $cursor   = $this->getIntParam($request, 'cursor', 0);

        $engine = new StreamEngine($streamId);
        $chunks = $engine->getFromChunk($cursor);

        return $this->success([
            'stream_id' => $streamId,
            'cursor'    => $cursor + count($chunks),
            'chunks'    => $chunks,
            'done'      => !empty($chunks) && end($chunks)['content'] === '[DONE]',
        ]);
    }

    // 对话操作

    public function quota(WP_REST_Request $request): WP_REST_Response
    {
        $userId = get_current_user_id();
        $role   = $this->getUserRole($userId);
        $limiter = new RateLimiter($role, $userId);

        return $this->success($limiter->getStats());
    }

    public function availableActions(WP_REST_Request $request): WP_REST_Response
    {
        $userId = get_current_user_id();
        $role   = $this->getUserRole($userId);

        $tools = \XZD\AI\V5\Tools\ToolRegistry::instance()->getAvailable($role);
        $actions = array_map(fn($t) => $t->toArray(), $tools);

        return $this->success([
            'actions' => $actions,
            'total'   => count($actions),
        ]);
    }

    public function execute(WP_REST_Request $request): WP_REST_Response
    {
        // 速率检查
        $limiter = RateLimiter::forCurrentUser();
        $check = $limiter->check();
        if (!$check['allowed']) {
            return $this->error($check['message'], 'rate_limited', 429);
        }

        $toolId = $this->getParam($request, 'tool_id', '');
        $params = $this->getJsonParam($request, 'params', []);
        $userId = get_current_user_id();

        // 游客（未登录）只能使用白名单中的只读工具
        if ($userId === 0) {
            $guestAllowedTools = [
                'wp_get_post', 'wp_search_posts', 'wp_list_terms',
                'search_knowledge',
            ];
            if (!in_array($toolId, $guestAllowedTools, true)) {
                return $this->error('请登录后使用此功能', 'login_required', 401);
            }
        }

        // 授权检查
        if ($err = $this->guardLicense('free')) return $err;

        $dispatcher = ActionDispatcher::instance();
        $result = $dispatcher->dispatch($toolId, $params, $userId);

        if ($result['success']) {
            $limiter->recordCost($result['cost'] ?? 0);
            return $this->success($result);
        }
        return $this->error($result['message'] ?? '执行失败', $result['code'] ?? 'execute_failed');
    }

    public function rateMessage(WP_REST_Request $request): WP_REST_Response
    {
        $messageId  = $this->getIntParam($request, 'message_id', 0);
        $rating     = $this->getIntParam($request, 'rating', 0);
        $sessionKey = $this->getParam($request, 'session_key', '');
        $userId     = get_current_user_id();
        $guestToken = $this->resolveGuestToken($request);

        if ($rating < 1 || $rating > 5) {
            return $this->error('评分范围为1-5', 'invalid_rating');
        }

        $sm = new SessionManager();
        $ok = $sm->rateMessage($messageId, $rating, $sessionKey, $userId, $guestToken);
        if (!$ok) {
            return $this->error('消息不存在或无权操作', 'message_not_found', 404);
        }

        return $this->success(null, '评价成功');
    }

    public function stopStream(WP_REST_Request $request): WP_REST_Response
    {
        $streamId = $this->getParam($request, 'stream_id', '');

        // 标记流为已停止
        if ($streamId) {
            set_transient("xzd_stream_stop_{$streamId}", 1, 60);
        }

        return $this->success(null, '已停止');
    }

    // 主动式 AI

    public function proactiveTrigger(WP_REST_Request $request): WP_REST_Response
    {
        $event    = $this->getParam($request, 'event', '');
        $pageUrl  = $this->getParam($request, 'page_url', '');
        $pageTitle = $this->getParam($request, 'page_title', '');
        $postId   = $this->getIntParam($request, 'post_id', 0);
        $pageType = $this->getParam($request, 'page_type', '');
        $userId   = get_current_user_id();
        $role     = $this->getUserRole($userId);

        // 授权检查
        if ($err = $this->guardLicense('free')) return $err;

        // 冷却检查
        $cooldownKey = "xzd_proactive_cooldown_{$event}_" . md5($pageUrl);
        if (get_transient($cooldownKey)) {
            return $this->success(['should_engage' => false, 'reason' => 'cooldown']);
        }

        // 游客扩展事件 — 增加深度阅读和多页面浏览引导注册
        $guestAllowed = ['first_visit', 'exit_intent', 'search_no_result', 'multi_page_view', 'scroll_depth_80'];
        if ($role === 'guest' && !in_array($event, $guestAllowed, true)) {
            return $this->success(['should_engage' => false, 'reason' => 'guest']);
        }

        // 使用 MonitorAgent 评估
        $monitor = new MonitorAgent();
        $result = $monitor->evaluate($event, [
            'page_url'   => $pageUrl,
            'page_title' => $pageTitle,
            'post_id'    => $postId,
            'page_type'  => $pageType,
            'user_id'    => $userId,
            'user_role'  => $role,
        ]);

        if ($result['should_engage'] ?? false) {
            set_transient($cooldownKey, 1, $result['cooldown_seconds'] ?? 300);
        }

        return $this->success(array_merge($result, ['event' => $event]));
    }

    public function proactiveSuggestions(WP_REST_Request $request): WP_REST_Response
    {
        $monitor = new MonitorAgent();
        $suggestions = $monitor->generateSuggestions();
        $health = $monitor->getHealthSummary();

        return $this->success([
            'suggestions' => $suggestions,
            'health'      => $health,
        ]);
    }

    // 内部方法

    /**
     * 获取用户角色标识 — 委托 UserHelper 统一判断
     */
    private function getUserRole(int $userId): string
    {
        return UserHelper::getRole($userId);
    }

    /**
     * 处理确认回复（危险操作/重复处理/图片来源选择）
     */
    private function handleConfirm(string $confirmKey, string $sessionKey, int $userId, string $guestToken = '', string $repeatChoice = ''): WP_REST_Response
    {
        // 图片来源选择 (key 以 src_ 开头)
        if (str_starts_with($confirmKey, 'src_')) {
            $data = get_transient('xzd_source_' . $confirmKey);
            if (!$data || !is_array($data)) {
                return $this->error('确认已过期，请重新操作', 'confirm_expired');
            }
            delete_transient('xzd_source_' . $confirmKey);

            $choice = $repeatChoice ?: 'public';
            // 设置用户选择的来源
            $params = $data['params'];
            $params['source'] = $choice;

            $dispatcher = ActionDispatcher::instance();
            $result = $dispatcher->dispatch($data['tool_id'], $params, $data['user_id'], $data['session_key']);

            $sourceLabel = ['ai' => 'AI 生图', 'stock' => '免费图库', 'public' => '公共域图库'][$choice] ?? $choice;
            $sm = new SessionManager();
            $sm->addMessage($sessionKey, 'assistant', "已选择「{$sourceLabel}」，执行结果：" . ($result['message'] ?? '完成'), [], $userId, $guestToken);

            return $this->success([
                'action'     => 'executed',
                'choice'     => $choice,
                'tool_id'    => $data['tool_id'],
                'result'     => $result,
            ], $result['message'] ?? '已执行');
        }

        // 重复处理确认 (key 以 rp_ 开头)
        if (str_starts_with($confirmKey, 'rp_')) {
            $data = get_transient('xzd_repeat_' . $confirmKey);
            if (!$data || !is_array($data)) {
                return $this->error('确认已过期，请重新操作', 'confirm_expired');
            }
            delete_transient('xzd_repeat_' . $confirmKey);

            $choice = $repeatChoice ?: 'skip';
            if ($choice === 'skip') {
                $sm = new SessionManager();
                $sm->addMessage($sessionKey, 'assistant', '已跳过，不重复处理。', [], $userId, $guestToken);
                return $this->success(['action' => 'skip', 'message' => '已跳过'], '已跳过');
            }

            // 设置处理模式
            $params = $data['params'];
            $params['_processing_mode'] = ($choice === 'append') ? 'append' : 'overwrite';
            $params['_repeat_choice'] = $choice;

            $dispatcher = ActionDispatcher::instance();
            $result = $dispatcher->dispatch($data['tool_id'], $params, $data['user_id'], $data['session_key']);

            $choiceLabel = ['overwrite' => '覆盖原有', 'append' => '追加共存'][$choice] ?? $choice;
            $sm = new SessionManager();
            $sm->addMessage($sessionKey, 'assistant', "已选择「{$choiceLabel}」，执行结果：" . ($result['message'] ?? '完成'), [], $userId, $guestToken);

            return $this->success([
                'action'     => 'executed',
                'choice'     => $choice,
                'tool_id'    => $data['tool_id'],
                'result'     => $result,
            ], $result['message'] ?? '已执行');
        }

        // 危险操作确认 (key 以 cf_ 开头) — 验证并重新执行工具
        if (str_starts_with($confirmKey, 'cf_')) {
            $data = get_transient('xzd_confirm_' . $confirmKey);
            if (!$data || !is_array($data)) {
                return $this->error('确认已过期，请重新操作', 'confirm_expired');
            }
            delete_transient('xzd_confirm_' . $confirmKey);

            // 设置 _confirmed 跳过二次确认，直接执行
            $params = $data['params'];
            $params['_confirmed'] = true;

            $dispatcher = ActionDispatcher::instance();
            $result = $dispatcher->dispatch($data['tool_id'], $params, $data['user_id'], $data['session_key']);

            $sm = new SessionManager();

            // 获取原始工具调用上下文，正确保存到会话
            $ctx = get_transient('xzd_confirm_ctx_' . $confirmKey);
            if ($ctx && is_array($ctx) && !empty($ctx['tool_call'])) {
                delete_transient('xzd_confirm_ctx_' . $confirmKey);
                $tc = $ctx['tool_call'];
                $tcId = $tc['id'] ?? '';

                // 保存助手消息（含 tool_call）
                $sm->addMessage($sessionKey, 'assistant', $ctx['ai_content'] ?? '', [
                    'tool_calls' => [$tc],
                ], $userId, $guestToken);

                // 保存工具执行结果
                $sm->addMessage($sessionKey, 'tool', json_encode($result, JSON_UNESCAPED_UNICODE), [
                    'tool_call_id' => $tcId,
                ], $userId, $guestToken);

                // 继续 ReAct 循环 — AI 继续后续步骤
                $role = UserHelper::getRole($data['user_id']);
                $loop = new \XZD\AI\V5\Agents\AgentLoop($this->orchestrator, $dispatcher);
                $continueResult = $loop->run($sessionKey, '请继续执行后续步骤', $role, $data['user_id'], $guestToken);

                return $this->success([
                    'action'           => 'confirmed',
                    'tool_id'          => $data['tool_id'],
                    'result'           => $result,
                    'continue_content' => $continueResult['content'] ?? '',
                    'continue_tool_calls' => $continueResult['tool_calls'] ?? null,
                    'continue_confirm_required' => $continueResult['confirm_required'] ?? false,
                    'continue_confirm_key'     => $continueResult['confirm_key'] ?? '',
                ], $result['message'] ?? '已执行');
            }

            // 无上下文（旧版兼容）：简单保存
            $sm->addMessage($sessionKey, 'assistant', '已确认执行「' . $data['tool_id'] . '」: ' . ($result['message'] ?? '完成'), [], $userId, $guestToken);

            return $this->success([
                'action'     => 'confirmed',
                'tool_id'    => $data['tool_id'],
                'result'     => $result,
            ], $result['message'] ?? '已执行');
        }

        // 未知确认类型
        return $this->success([
            'message'     => '收到确认，正在执行...',
            'session_key' => $sessionKey,
        ]);
    }

    private function setSSEHeaders(string $streamId): void
    {
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');
        header('Connection: keep-alive');
        header('X-Accel-Buffering: no');
        header('X-Stream-Id: ' . $streamId);

        // 关闭输出缓冲
        if (ob_get_level()) {
            ob_end_clean();
        }
    }

    private function emitSSE(array $data, string $event = 'message'): void
    {
        echo "event: {$event}\n";
        echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n\n";
        if (ob_get_level()) {
            ob_flush();
        }
        flush();
    }

    /**
     * 选择流式 Provider（含熔断逻辑）
     */
    private function selectStreamProvider(): ?\XZD\AI\V5\AI\ProviderInterface
    {
        $config = \XZD\AI\V5\Core\Config::instance();
        $connections = $config->get('connections.text', []);

        if (empty($connections)) {
            return null;
        }

        // 使用统一排序方法
        \XZD\AI\V5\AI\Client::sortConnections($connections);

        // 熔断器
        $circuitBreaker = \XZD\AI\V5\Core\Config::getArrayTransient('xzd_ai_circuit_breaker');
        $now = time();

        foreach ($connections as $i => $conn) {
            if (!empty($conn['api_key']) && ($conn['is_enabled'] ?? true)) {
                // 熔断检查
                $breakerKey = "text_{$i}";
                if (isset($circuitBreaker[$breakerKey])) {
                    $f = $circuitBreaker[$breakerKey];
                    if ($f['count'] >= 2 && ($now - $f['since']) < 60) {
                        continue;
                    }
                }

                // 解密 api_key
                if (str_starts_with($conn['api_key'], XZD_AI_ENC_PREFIX)) {
                    $conn['api_key'] = $config->decrypt($conn['api_key']);
                }
                return \XZD\AI\V5\AI\ProviderFactory::create($conn);
            }
        }

        return null;
    }

    /**
     * 按角色生成追问建议
     *
     * 根据用户角色和AI回复内容生成3个追问pill，不同角色看到不同类型的追问。
     */
    private function generateFollowups(string $role, string $aiContent): array
    {
        $followups = [];

        $rolePatterns = match ($role) {
            'admin' => [
                '批量处理'   => '能帮我批量处理最近的文章吗？',
                'SEO'       => '帮我分析一下网站SEO状况',
                '数据'      => '生成一份最近7天的数据报告',
                '标签'      => '给没有标签的文章批量生成标签',
                '错误'      => '查看最近的错误日志',
                '队列'      => '查看当前队列状态',
                '用户'      => '最近注册了哪些新用户？',
                '插件'      => '检查插件更新状态',
            ],
            'user', 'vip' => [
                '写'        => '帮我写一篇新文章',
                '文章'      => '查看我发布的文章',
                '积分'      => '查看我的积分余额',
                '签到'      => '帮我签到',
                'VIP'       => '查看我的VIP状态',
                '标签'      => '给我的文章生成标签',
                'SEO'       => '帮我优化文章SEO',
                '搜索'      => '搜索站内相关内容',
            ],
            default => [ // guest
                '搜索'      => '搜索关于WordPress的文章',
                '介绍'      => '介绍一下这个网站',
                '热门'      => '最近有什么热门内容？',
                '注册'      => '注册会员有什么好处？',
                '功能'      => '这个网站有哪些功能？',
                '分类'      => '网站有哪些内容分类？',
            ],
        };

        $text = strip_tags($aiContent);
        $added = [];
        foreach ($rolePatterns as $keyword => $followup) {
            if (count($followups) >= 3) break;
            if (mb_strpos($text, $keyword) !== false && !in_array($followup, $added)) {
                $followups[] = $followup;
                $added[] = $followup;
            }
        }

        // 不够3条→补充与该角色相关的通用追问
        $fallbacks = match ($role) {
            'admin'   => ['查看站点统计数据', '检查队列和自动化状态', '管理用户和评论'],
            'user','vip' => ['还有什么AI功能可以帮到我？', '帮我查看最近的内容', '能推荐一些热门文章吗？'],
            default   => ['这个网站是做什么的？', '怎么注册账号？', '能推荐一些文章吗？'],
        };
        foreach ($fallbacks as $fb) {
            if (count($followups) >= 3) break;
            if (!in_array($fb, $followups)) $followups[] = $fb;
        }

        return $followups;
    }

}
