<?php

namespace XZD\AI\V5\Agents;

use XZD\AI\V5\Tools\ToolRegistry;
use XZD\AI\V5\Tools\ToolResult;
use XZD\AI\V5\Core\Logger;
use XZD\AI\V5\Core\Config;

/**
 * 工具调度器 — 安全执行 + 确认模式 + 审计日志
 */
class ActionDispatcher
{
    private static ?self $instance = null;

    /** 待确认的操作缓存（Transient） */
    private const CONFIRM_PREFIX = 'xzd_confirm_';
    private const CONFIRM_TTL = 300; // 5分钟

    public static function instance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * 执行工具调用（核心入口）
     *
     * @return array ['success','confirm_required','confirm_key','data','message']
     */
    public function dispatch(string $toolId, array $params, int $userId, string $sessionKey = ''): array
    {
        $tool = ToolRegistry::instance()->get($toolId);
        if (!$tool) {
            return ['success' => false, 'message' => "工具不存在: {$toolId}", 'code' => 'tool_not_found'];
        }

        // 危险操作需要确认
        if ($tool->danger === 'high' && empty($params['_confirmed'])) {
            return $this->requestConfirm($toolId, $params, $userId, $sessionKey);
        }

        // 中等危险 + 首次确认模式
        if ($tool->danger === 'medium' && $tool->confirmMode === 'single' && empty($params['_confirmed'])) {
            return $this->requestConfirm($toolId, $params, $userId, $sessionKey);
        }

        // 验证确认密钥
        if (!empty($params['_confirm_key'])) {
            $verified = $this->verifyConfirm($params['_confirm_key'], $toolId, $userId);
            if (!$verified) {
                return ['success' => false, 'message' => '确认已过期或无效，请重新确认', 'code' => 'confirm_invalid'];
            }
        }

        // 清理确认相关参数
        unset($params['_confirmed'], $params['_confirm_key']);

        // 用户手动触发的操作默认使用填充模式（已处理则跳过），明确说"覆盖/重新"时走覆盖模式
        if (!isset($params['_processing_mode'])) {
            $params['_processing_mode'] = 'fill_empty';
        }

        // 执行
        $result = ToolRegistry::instance()->execute($toolId, $params, $userId);

        // 写入操作日志（供撤销等功能使用）
        $this->logOperation($toolId, $tool->name, $tool->category, $params, $result, $userId, $sessionKey, $tool->undoWindow ?? 0);

        return $this->formatResult($result, $tool);
    }

    /**
     * 批量执行工具
     *
     * @param array $calls [['tool_id'=>'...','params'=>[...]], ...]
     * @return array
     */
    public function dispatchBatch(array $calls, int $userId, string $sessionKey = ''): array
    {
        $results = [];
        foreach ($calls as $i => $call) {
            $results[$i] = $this->dispatch(
                $call['tool_id'] ?? $call['id'] ?? '',
                $call['params'] ?? [],
                $userId,
                $sessionKey
            );
        }
        return $results;
    }

    /**
     * 撤销操作
     */
    public function undo(int $operationId, int $userId): array
    {
        global $wpdb;
        $table = $wpdb->prefix . 'xzd_ai_operation_log';

        $op = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$table} WHERE id = %d AND is_undone = 0",
            $operationId
        ), ARRAY_A);

        if (!$op) {
            return ['success' => false, 'message' => '操作不存在或已撤销'];
        }

        // 检查撤销窗口
        if ($op['undo_window'] > 0) {
            $createdAt = strtotime($op['created_at']);
            $elapsed = time() - $createdAt;
            if ($elapsed > $op['undo_window']) {
                return ['success' => false, 'message' => '撤销窗口已过期'];
            }
        }

        $undoData = json_decode($op['undo_data'] ?? '{}', true);
        if (empty($undoData)) {
            return ['success' => false, 'message' => '该操作不支持撤销'];
        }

        // 尝试执行反向操作
        if (!empty($undoData['reverse_tool'])) {
            $result = ToolRegistry::instance()->execute($undoData['reverse_tool'], $undoData['reverse_params'] ?? [], $userId);
            if ($result->success) {
                $wpdb->update($table, [
                    'is_undone'  => 1,
                    'undone_at'  => current_time('mysql'),
                ], ['id' => $operationId]);

                Logger::info('undo', "操作 #{$operationId} 已撤销", ['user_id' => $userId]);
                return ['success' => true, 'message' => '操作已撤销'];
            }

            return ['success' => false, 'message' => '撤销失败: ' . $result->message];
        }

        return ['success' => false, 'message' => '该操作不支持自动撤销'];
    }

    /**
     * 请求确认
     */
    private function requestConfirm(string $toolId, array $params, int $userId, string $sessionKey): array
    {
        $confirmKey = 'cf_' . bin2hex(random_bytes(12));
        $confirmData = [
            'tool_id'     => $toolId,
            'params'      => $params,
            'user_id'     => $userId,
            'session_key' => $sessionKey,
            'created_at'  => time(),
        ];

        set_transient(self::CONFIRM_PREFIX . $confirmKey, $confirmData, self::CONFIRM_TTL);

        $tool = ToolRegistry::instance()->get($toolId);

        return [
            'success'          => false,
            'code'             => 'confirm_required',
            'message'          => "操作「{$tool->name}」需要用户确认后才能执行。请告知用户：此操作将{$tool->description}，用户需在" . self::CONFIRM_TTL . "秒内点击确认按钮。",
            'confirm_required' => true,
            'confirm_key'      => $confirmKey,
            'confirm_message'  => "⚠️ 危险操作确认: {$tool->name}",
            'confirm_detail'   => "即将执行: {$tool->description}\n参数: " . json_encode($params, JSON_UNESCAPED_UNICODE),
            'confirm_timeout'  => self::CONFIRM_TTL,
            'tool_id'          => $toolId,
        ];
    }

    /**
     * 验证确认密钥
     */
    private function verifyConfirm(string $confirmKey, string $toolId, int $userId): bool
    {
        $data = get_transient(self::CONFIRM_PREFIX . $confirmKey);
        if (!$data || !is_array($data)) {
            return false;
        }

        if ($data['tool_id'] !== $toolId) {
            return false;
        }

        if ($data['user_id'] !== $userId) {
            return false;
        }

        delete_transient(self::CONFIRM_PREFIX . $confirmKey);
        return true;
    }

    /**
     * 写入统一操作日志
     */
    private function logOperation(string $toolId, string $toolName, string $category, array $params, ToolResult $result, int $userId, string $sessionKey, int $undoWindow): void
    {
        global $wpdb;
        $table = $wpdb->prefix . 'xzd_ai_operation_log';

        $targetType = null;
        $targetId = null;

        // 尝试从参数中提取目标信息
        if (!empty($params['post_id'])) {
            $targetType = 'post';
            $targetId = (int) $params['post_id'];
        } elseif (!empty($params['user_id'])) {
            $targetType = 'user';
            $targetId = (int) $params['user_id'];
        } elseif (!empty($params['comment_id'])) {
            $targetType = 'comment';
            $targetId = (int) $params['comment_id'];
        }

        $wpdb->insert($table, [
            'tool_id'       => $toolId,
            'tool_name'     => $toolName,
            'category'      => $category,
            'target_type'   => $targetType,
            'target_id'     => $targetId,
            'params'        => wp_json_encode($this->sanitizeParams($params)),
            'result'        => wp_json_encode($result->toArray()),
            'status'        => $result->success ? 'success' : 'failed',
            'error_message' => $result->success ? null : $result->message,
            'undo_data'     => wp_json_encode($result->undoData),
            'undo_window'   => $undoWindow,
            'user_id'       => $userId,
            'session_key'   => $sessionKey ?: null,
            'ip'            => $_SERVER['REMOTE_ADDR'] ?? '',
            'cost_tokens'   => $result->tokensUsed,
            'cost_rmb'      => $result->costRmb,
            'duration_ms'   => 0,
            'created_at'    => current_time('mysql'),
        ]);
    }

    /**
     * 脱敏参数
     */
    private function sanitizeParams(array $params): array
    {
        $sensitive = ['api_key', 'password', 'pass', 'secret', 'token', '_confirm_key'];
        foreach ($sensitive as $key) {
            if (isset($params[$key])) {
                $params[$key] = '***';
            }
        }
        return $params;
    }

    /**
     * 格式化返回结果
     */
    private function formatResult(ToolResult $result, $tool): array
    {
        $out = [
            'success'        => $result->success,
            'data'           => $result->data,
            'message'        => $result->message,
            'code'           => $result->errorCode,
            'undo_available' => !empty($result->undoData),
            'confirm_required' => false,
        ];

        if ($result->success && $tool->undoWindow > 0) {
            $out['notice'] = "操作成功，{$tool->undoWindow}秒内可撤销";
        }

        return $out;
    }
}
