<?php

namespace XZD\AI\V5\Tools;

use XZD\AI\V5\Core\Logger;
use XZD\AI\V5\Core\Config;
use XZD\AI\V5\Core\UserHelper;
use XZD\AI\V5\Theme\AdapterRegistry;

/**
 * 工具注册表 — 集中管理所有工具（注册/查询/执行/AI格式输出/权限检查）
 *
 * @example
 *   ToolRegistry::instance()->register('my_tool', [...])->execute('my_tool', [...], $userId);
 */
class ToolRegistry
{
    private static ?self $instance = null;

    /** @var Tool[] */
    private array $tools = [];

    private array $stats = [];

    private bool $initialized = false;

    /**
     * 系统调用上下文标志 — 内部代码（PipelineEngine等）调用时设为true
     * userId=0 才会被识别为系统调用并跳过权限检查
     */
    private static bool $systemCallContext = false;

    /**
     * 进入系统调用上下文，返回进入前的状态用于恢复
     */
    public static function enterSystemContext(): bool
    {
        $prev = self::$systemCallContext;
        self::$systemCallContext = true;
        return $prev;
    }

    /**
     * 恢复系统调用上下文
     */
    public static function restoreSystemContext(bool $previous): void
    {
        self::$systemCallContext = $previous;
    }

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

    private function ensureInitialized(): void
    {
        if ($this->initialized) return;
        ToolRegistrar::registerAll();
        $this->initialized = true;
    }

    /**
     * 注册一个工具
     */
    public function register(string $id, array $config): self
    {
        $this->tools[$id] = new Tool(
            id: $id,
            name: $config['name'] ?? $id,
            description: $config['description'] ?? '',
            category: $config['category'] ?? 'content',
            danger: $config['danger'] ?? 'none',
            wpCap: $config['wp_cap'] ?? 'read',
            minRole: $config['min_role'] ?? 'guest',
            themeRequired: $config['theme_required'] ?? false,
            capabilityKey: $config['capability_key'] ?? null,
            confirmMode: $config['confirm_mode'] ?? 'auto',
            undoWindow: $config['undo_window'] ?? 0,
            schema: $config['schema'] ?? [],
            callback: $config['callback'],
            pipelineStage: $config['pipeline_stage'] ?? false,
            pipelineEvent: $config['pipeline_event'] ?? 'publish_post',
            pipelineAsync: $config['pipeline_async'] ?? false,
            trackProcessing: $config['track_processing'] ?? false,
            supportsOverwrite: $config['supports_overwrite'] ?? false,
        );

        return $this;
    }

    // 查询

    public function has(string $id): bool
    {
        $this->ensureInitialized();
        return isset($this->tools[$id]);
    }

    public function get(string $id): ?Tool
    {
        $this->ensureInitialized();
        return $this->tools[$id] ?? null;
    }

    /** @return Tool[] */
    public function getAll(): array
    {
        $this->ensureInitialized();
        return $this->tools;
    }

    /** @return Tool[] */
    public function getByCategory(string $category): array
    {
        $this->ensureInitialized();
        return array_filter($this->tools, fn(Tool $t) => $t->category === $category);
    }

    /** 获取当前角色 + 授权等级下可用的工具 */
    public function getAvailable(string $role = 'guest'): array
    {
        $this->ensureInitialized();
        $roleLevel = ['guest' => 0, 'user' => 1, 'vip' => 2, 'admin' => 3];
        $userLevel = $roleLevel[$role] ?? 0;

        $roleFiltered = array_filter($this->tools, function (Tool $t) use ($userLevel, $roleLevel) {
            $minLevel = $roleLevel[$t->minRole] ?? 0;
            return $userLevel >= $minLevel;
        });

        // 三级权限过滤：按授权等级移除不可用工具
        $licenseLevel = \XZD\AI\V5\Core\LicenseService::instance()->getLevel();
        $blocked = \XZD\AI\V5\Core\ToolPermissions::getBlockedToolsForLevel($licenseLevel);

        return array_filter($roleFiltered, fn(Tool $t) => !in_array($t->id, $blocked, true));
    }

    /** 获取流水线可用阶段（含授权等级过滤） */
    public function getPipelineStages(string $event): array
    {
        $this->ensureInitialized();
        $stages = array_filter($this->tools, fn(Tool $t) =>
            $t->pipelineStage && $t->pipelineEvent === $event
        );

        // 按授权等级过滤流水线阶段
        $licenseLevel = \XZD\AI\V5\Core\LicenseService::instance()->getLevel();
        return array_filter($stages, fn(Tool $t) =>
            \XZD\AI\V5\Core\ToolPermissions::isPipelineStageAllowed($t->id, $licenseLevel)
        );
    }

    /**
     * 自动化规则中禁用的危险工具（不可逆操作，不允许在自动化规则中作为动作使用）
     */
    private const BLOCKED_AUTOMATION_TOOLS = [
        'wp_delete_post', 'wp_delete_user', 'wp_change_role', 'wp_create_user',
        'theme_set_vip', 'theme_remove_vip', 'theme_ban_user', 'theme_unban_user',
        'theme_add_points', 'theme_deduct_points', 'theme_add_balance', 'theme_deduct_balance',
        'set_all_tools_for_role', 'toggle_pipeline_trigger',
    ];

    /** 获取自动化规则可用动作 */
    public function getAutomationActions(): array
    {
        $this->ensureInitialized();
        return array_filter($this->tools, fn(Tool $t) =>
            $t->category !== 'automation'
            && !in_array($t->id, self::BLOCKED_AUTOMATION_TOOLS, true)
        );
    }

    // AI 格式输出

    /** OpenAI function calling 工具列表（按授权等级过滤） */
    public function toOpenAIFunctions(string $role = 'admin'): array
    {
        $this->ensureInitialized();
        $available = $this->getAvailable($role);
        return array_values(array_map(fn(Tool $t) => $t->toOpenAIFunction(), $available));
    }

    /** 生成提示词中的工具描述文本 */
    public function toToolDescription(string $role = 'admin'): string
    {
        $this->ensureInitialized();
        $readLines = [];
        $writeLines = [];
        foreach ($this->getAvailable($role) as $tool) {
            $line = "- **{$tool->id}** — {$tool->name}: {$tool->description}";
            if ($tool->needsConfirm()) {
                $writeLines[] = "[管理] {$line}";
            } else {
                $readLines[] = "[只读] {$line}";
            }
        }
        $sections = [];
        if (!empty($readLines)) {
            $sections[] = "## 只读工具（无需确认，直接执行）\n" . implode("\n", $readLines);
        }
        if (!empty($writeLines)) {
            $sections[] = "## 管理工具（需确认或需管理员权限）\n" . implode("\n", $writeLines);
        }
        return implode("\n\n", $sections);
    }

    // 执行

    /**
     * 执行工具调用
     */
    public function execute(string $toolId, array $params, int $userId = 0): ToolResult
    {
        $this->ensureInitialized();
        $start = microtime(true);

        $tool = $this->get($toolId);
        if (!$tool) {
            return ToolResult::error("工具不存在: {$toolId}", 'tool_not_found');
        }

        // 授权等级检查（三级权限）
        $licenseLevel = \XZD\AI\V5\Core\LicenseService::instance()->getLevel();
        if (!\XZD\AI\V5\Core\ToolPermissions::isToolAvailable($toolId, $licenseLevel)) {
            $requiredLevel = \XZD\AI\V5\Core\ToolPermissions::getRequiredLevel($toolId);
            $levelInfo = \XZD\AI\V5\Core\ToolPermissions::getLevelInfo($requiredLevel);
            return ToolResult::error(
                "「{$tool->name}」需要{$levelInfo['name']}授权，请升级后使用",
                'license_level_insufficient'
            );
        }

        // 权限检查
        $permCheck = $this->checkPermission($tool, $userId);
        if (!$permCheck->success) {
            return $permCheck;
        }

        // 内容工具文章归属检查 — 系统调用/管理员跳过，普通用户只能操作自己的文章
        $trackingPostId = $this->extractTargetPostId($tool, $params);
        if ($trackingPostId > 0 && $tool->category === 'content') {
            $post = get_post($trackingPostId);
            // 系统调用（systemCallContext=true）跳过归属检查；管理员跳过；否则检查文章归属
            if ($post && !self::$systemCallContext && $userId > 0 && !current_user_can('manage_options') && (int) $post->post_author !== $userId) {
                return ToolResult::error('只能操作自己发布的文章', 'forbidden');
            }
        }

        // 处理标记检查（fill_empty模式检测到已处理时，返回 repeat_detected 让前端询问用户）
        $mode = $params['_processing_mode'] ?? 'fill_empty';
        if ($tool->trackProcessing && $trackingPostId && $mode === 'fill_empty') {
            // generate_tags 特殊处理 — 检测手动标签
            if ($toolId === 'generate_tags') {
                $currentTags = wp_get_post_tags($trackingPostId, ['fields' => 'names']);
                if (!empty($currentTags)) {
                    $aiTagList = get_post_meta($trackingPostId, '_xzd_ai_tags_list', true) ?: [];
                    $manualTags = array_diff($currentTags, $aiTagList);
                    if (!empty($manualTags)) {
                        return ToolResult::success([
                            'repeat_detected' => true,
                            'tool_id'         => 'generate_tags',
                            'tool_name'       => $tool->name,
                            'post_id'         => $trackingPostId,
                            'manual_tags'     => array_values($manualTags),
                            'choices' => [
                                ['key' => 'skip',      'label' => '跳过不处理（推荐）', 'desc' => '检测到手动标签：' . implode('、', array_slice($manualTags, 0, 5))],
                                ['key' => 'append',    'label' => '追加共存', 'desc' => '保留手动标签，追加AI生成的新标签'],
                                ['key' => 'overwrite', 'label' => '覆盖原有', 'desc' => '清除所有现有标签，重新生成'],
                            ],
                        ], '检测到手动标签，已跳过处理');
                    }
                }
            }

            $marker = get_post_meta($trackingPostId, '_xzd_ai_processed_' . $toolId, true);
            if (!empty($marker) && ($marker['status'] ?? '') === 'completed') {
                $lastTime = $marker['timestamp'] ?? '未知时间';
                $lastMode = $marker['mode'] ?? 'ai';
                $lastSummary = $marker['summary'] ?? '';
                return ToolResult::success([
                    'repeat_detected' => true,
                    'tool_id'         => $toolId,
                    'tool_name'       => $tool->name,
                    'post_id'         => $trackingPostId,
                    'last_time'       => $lastTime,
                    'last_mode'       => $lastMode,
                    'last_summary'    => $lastSummary,
                    'choices' => [
                        ['key' => 'overwrite', 'label' => '覆盖原有', 'desc' => '清除旧数据，重新生成'],
                        ['key' => 'append',    'label' => '追加共存', 'desc' => '保留原有，追加新内容'],
                        ['key' => 'skip',      'label' => '跳过不处理', 'desc' => '保持现状'],
                    ],
                ], "文章 {$trackingPostId} 在 {$lastTime} 已处理过「{$tool->name}」" . ($lastSummary ? "：{$lastSummary}" : ''));
            }
        }

        // 保留 _processing_mode 供工具回调区分覆盖/追加模式

        // 执行
        try {
            $callback = $tool->callback;
            if (!is_callable($callback)) {
                return ToolResult::error("工具回调不可调用: {$toolId}", 'callback_invalid');
            }

            $result = $callback($params, $userId);

            // 统一返回格式
            if ($result instanceof ToolResult) {
                $final = $result;
            } elseif (is_array($result)) {
                $final = ($result['success'] ?? true)
                    ? ToolResult::success($result['data'] ?? $result, $result['message'] ?? 'ok')
                    : ToolResult::error($result['message'] ?? 'failed', $result['code'] ?? 'error');
            } else {
                $final = ToolResult::success($result);
            }
        } catch (\Throwable $e) {
            Logger::error($toolId, $e->getMessage(), ['params' => $params]);
            $final = ToolResult::error($e->getMessage(), 'exception');
        }

        // 写入处理标记（防止重复处理）
        // 来源选择 / 重复检测不算完成，不写标记
        $isSourceSelect = ($final->data['source_selection'] ?? false);
        if ($tool->trackProcessing && $trackingPostId && $final->success && !$isSourceSelect) {
            update_post_meta($trackingPostId, '_xzd_ai_processed_' . $toolId, wp_json_encode([
                'status'      => 'completed',
                'mode'        => $mode,
                'timestamp'   => current_time('mysql'),
                'operator_id' => $userId,
                'summary'     => $final->message,
            ]));
        }

        // 记录统计
        $this->stats[$toolId] = ($this->stats[$toolId] ?? 0) + 1;

        // 记录日志 — 流水线上下文内跳过单工具日志，统一在流水线完成时写汇总
        if (!Logger::isPipelineContext()) {
            Logger::operation([
                'tool_id'     => $toolId,
                'tool_name'   => $tool->name,
                'category'    => $tool->category,
                'params'      => $params,
                'result'      => $final->toArray(),
                'status'      => $final->success ? 'success' : 'failed',
                'error_message' => $final->success ? null : $final->message,
                'undo_data'   => $final->undoData,
                'undo_window' => $tool->undoWindow,
                'user_id'     => $userId,
                'duration_ms' => (int) ((microtime(true) - $start) * 1000),
                'cost_tokens' => $final->tokensUsed,
                'cost_rmb'    => $final->costRmb,
            ]);
        }

        return $final;
    }

    // 统计

    public function getStats(): array
    {
        $this->ensureInitialized();
        return [
            'total_tools' => count($this->tools),
            'call_counts' => $this->stats,
            'by_category' => array_reduce(
                array_keys($this->tools),
                fn(array $c, string $id) => array_merge($c, [
                    $this->tools[$id]->category => ($c[$this->tools[$id]->category] ?? 0) + 1,
                ]),
                []
            ),
        ];
    }

    // 内部方法

    /**
     * 从工具参数中提取文章ID（用于处理标记）
     */
    private function extractTargetPostId(Tool $tool, array $params): int
    {
        $postId = (int)($params['post_id'] ?? 0);
        if ($postId > 0) {
            return $postId;
        }
        return 0;
    }

    private function checkPermission(Tool $tool, int $userId): ToolResult
    {
        // 仅当系统调用上下文标志为 true 时才允许 userId=0 跳过权限检查
        if ($userId === 0 && self::$systemCallContext) {
            return ToolResult::success(null, 'system_bypass');
        }

        // 游客（未登录用户，userId=0）按 guest 角色处理
        if ($userId === 0) {
            $guestLevel = UserHelper::getRoleLevel('guest');
            $minLevel = UserHelper::getRoleLevel($tool->minRole);
            if ($guestLevel < $minLevel) {
                return ToolResult::error("请登录后使用此功能", 'login_required');
            }
            // guest 角色跳过 wpCap/主题适配检查（guest 无 WP 权限）
            $blockedByRole = Config::instance()->get('tools.blocked_tools_by_role', []);
            $guestBlocked = $blockedByRole['guest'] ?? [];
            if (in_array($tool->id, $guestBlocked, true)) {
                return ToolResult::error("此功能未对「游客」角色开放，请联系管理员", 'tool_blocked');
            }
            return ToolResult::success(null, 'guest_allowed');
        }

        // 管理员跳过检查
        if (current_user_can('manage_options')) {
            return ToolResult::success(null, 'admin_bypass');
        }

        // 角色层级检查
        $userRole = UserHelper::getRole($userId);
        $userLevel = UserHelper::getRoleLevel($userRole);
        $minLevel = UserHelper::getRoleLevel($tool->minRole);

        if ($userLevel < $minLevel) {
            return ToolResult::error("权限不足: 需要 {$tool->minRole} 角色", 'insufficient_role');
        }

        // WordPress 权限检查
        if ($tool->wpCap && $tool->wpCap !== 'read') {
            if ($userId && !user_can($userId, $tool->wpCap)) {
                return ToolResult::error("权限不足: 需要 {$tool->wpCap}", 'insufficient_cap');
            }
        }

        // 主题适配检查
        if ($tool->themeRequired) {
            $adapter = AdapterRegistry::getCurrent();
            if ($tool->capabilityKey) {
                $cap = $adapter->getCapabilities()[$tool->capabilityKey] ?? null;
                if (!$cap || !$cap['available']) {
                    return ToolResult::error(
                        $cap['reason'] ?? '当前主题未适配此功能，需要子比主题',
                        'theme_not_adapted'
                    );
                }
            }
        }

        // 分角色黑名单检查 — 角色够但被管理员关闭 -> 提示可申请
        $blockedByRole = Config::instance()->get('tools.blocked_tools_by_role', []);
        $roleBlocked = $blockedByRole[$userRole] ?? [];
        if (in_array($tool->id, $roleBlocked, true)) {
            return ToolResult::error("此功能未对「{$userRole}」角色开放，可联系管理员申请开通", 'tool_blocked');
        }

        return ToolResult::success(null, 'allowed');
    }

}
