<?php

namespace XZD\AI\V5\Core;

use XZD\AI\V5\Admin\AdminPage;
use XZD\AI\V5\Database\Schema;
use XZD\AI\V5\REST\Router;
use XZD\AI\V5\REST\Controllers\HealthController;
use XZD\AI\V5\REST\Controllers\SettingsController;
use XZD\AI\V5\REST\Controllers\AIConnectionController;
use XZD\AI\V5\REST\Controllers\ToolController;
use XZD\AI\V5\REST\Controllers\ChatMessageController;
use XZD\AI\V5\REST\Controllers\LogController;
use XZD\AI\V5\REST\Controllers\LicenseController;
use XZD\AI\V5\REST\Controllers\PipelineController;
use XZD\AI\V5\REST\Controllers\AutomationController;
use XZD\AI\V5\REST\Controllers\ThemeController;
use XZD\AI\V5\REST\Controllers\AnalyticsController;
use XZD\AI\V5\Tools\ToolRegistrar;
use XZD\AI\V5\Tools\BatchPipeline;
use XZD\AI\V5\Tools\EventTrigger;
use XZD\AI\V5\Tools\RuleEngine;
use XZD\AI\V5\Admin\PostEditorAI;
use XZD\AI\V5\UI\FloatingChat;
use XZD\AI\V5\UI\SummaryRenderer;
use XZD\AI\V5\Universal\MetaRenderer;
use XZD\AI\V5\Universal\SitemapGenerator;
use XZD\AI\V5\Universal\LLMSTxtGenerator;
use XZD\AI\V5\SEO\SEOMetaBox;
use XZD\AI\V5\Theme\Integrations\ZibllPaymentIntegration;

use XZD\AI\V5\Theme\Integrations\ZibllFrontendEditorIntegration;
use XZD\AI\V5\Theme\Integrations\ZibllBBSIntegration;
use XZD\AI\V5\Theme\TagStyleManager;
use XZD\AI\V5\REST\Controllers\TagStyleController;

/**
 * 插件启动类 — 模块注册 / 按序加载 / 生命周期
 */
class Plugin
{
    private static ?self $instance = null;

    /** 已注册的模块列表 */
    private array $modules = [];

    /**
     * 获取单例
     */
    public static function instance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct()
    {
        $this->registerDefaultModules();
    }

    /**
     * 启动插件
     */
    public function boot(): void
    {
        // 注册 WP-CLI 命令
        CLI::register();

        // AI 响应缓存在 shutdown 时持久化
        add_action('shutdown', [AiResponseCache::class, 'persist']);

        // 文本域延迟到 init 加载（WP 6.7+ 要求）
        add_action('init', function () {
            load_plugin_textdomain('xzdbk-ai', false, dirname(plugin_basename(XZD_AI_FILE)) . '/languages');
        });

        // 异步触发心跳，高负载时跳过selfPing避免PHP-FPM进程爆炸
        add_action('init', function () {
            // REST请求不触发selfPing防止递归；每核负载超阈值时跳过
            if (defined('REST_REQUEST') && REST_REQUEST) return;
            if (self::getCpuAdjustedLoad() > 2.0) return;
            if (\XZD\AI\V5\Core\Heartbeat::isOverdue()) {
                \XZD\AI\V5\Core\Heartbeat::selfPing();
            }
        }, 5);

        // 工具注册表延迟加载，首次访问时自动初始化

        // 日期保护过滤器
        PreserveDates::init();

        // 注册所有 WordPress 钩子
        EventTrigger::init();

        // 注册自定义 Cron 周期
        add_action('init', function () {
            add_filter('cron_schedules', function (array $schedules): array {
                $schedules['every_30_seconds'] = [
                    'interval' => 30,
                    'display'  => __('每30秒', 'xzdbk-ai'),
                ];
                $schedules['every_minute'] = [
                    'interval' => 60,
                    'display'  => __('每1分钟', 'xzdbk-ai'),
                ];
                $schedules['every_2_minutes'] = [
                    'interval' => 120,
                    'display'  => __('每2分钟', 'xzdbk-ai'),
                ];
                $schedules['every_5_minutes'] = [
                    'interval' => 300,
                    'display'  => __('每5分钟', 'xzdbk-ai'),
                ];
                $schedules['every_10_minutes'] = [
                    'interval' => 600,
                    'display'  => __('每10分钟', 'xzdbk-ai'),
                ];
                $schedules['every_15_minutes'] = [
                    'interval' => 900,
                    'display'  => __('每15分钟', 'xzdbk-ai'),
                ];
                $schedules['every_30_minutes'] = [
                    'interval' => 1800,
                    'display'  => __('每30分钟', 'xzdbk-ai'),
                ];
                return $schedules;
            });
        });

        // 配置变更时通知前端刷新
        add_action('updated_option', function (string $option, $old, $new) {
            if ($option === 'xzd_ai_settings') {
                set_transient('xzd_ai_config_changed', time(), 120);
            }
        }, 10, 3);

        // 1. 激活/迁移检查 / 卸载清理
        register_activation_hook(XZD_AI_FILE, [$this, 'activate']);
        register_deactivation_hook(XZD_AI_FILE, [$this, 'deactivate']);
        register_uninstall_hook(XZD_AI_FILE, [self::class, 'uninstall']);

        // V3.2: 自动学习——心跳设置标记，下次页面访问时悄悄执行
        // 不依赖 AS Runner，不依赖 HTTP 自调用，用户无感知
        add_action('init', function () {
            if (get_transient('xzd_learning_pending')) {
                delete_transient('xzd_learning_pending');
                // 只在不阻塞用户的前提下执行
                if (\XZD\AI\V5\Core\LicenseService::instance()->getLevel() === 'ultimate'
                    && class_exists(\XZD\AI\V5\Tools\LearningEngine::class)) {
                    // 先返回响应给用户
                    if (function_exists('fastcgi_finish_request')) {
                        fastcgi_finish_request();
                    }
                    ignore_user_abort(true);
                    set_time_limit(600);
                    ob_end_flush();
                    flush();
                    // 后台执行学习
                    $engine = \XZD\AI\V5\Tools\LearningEngine::instance();
                    $engine->reseedKnowledgeBase();
                    $engine->learnSiteStructure();
                    $engine->generateSiteFAQ();
                    $engine->generateProactiveTemplates();
                    $engine->run('incremental');
                    if (date('N') == 7) {
                        $engine->cleanupExpired();
                    }
                }
            }
        }, 5);

        // 2. 初始化各模块
        add_action('plugins_loaded', function () {
            $this->initModules();
        });

        // 3. 智能补种预设规则 — 每小时检查一次
        add_action('init', function () {
            $seedKey = 'xzd_last_seed_check';
            $lastCheck = get_transient($seedKey);
            if (!$lastCheck) {
                set_transient($seedKey, time(), HOUR_IN_SECONDS);
                if (class_exists(RuleEngine::class)) {
                    RuleEngine::instance()->seedPresets();
                }
            }
        });

        // 4. 批量处理 Cron 钩子
        add_action('xzd_ai_process_batch', function (string $batchId) {
            (new BatchPipeline())->processChunk($batchId);
        }, 10, 1);

        // 4b. Action Scheduler 回调
        add_action('xzd_worker_process', function (array $args) {
            if (class_exists(\XZD\AI\V5\Tools\PostWorker::class)) {
                \XZD\AI\V5\Tools\PostWorker::process($args);
            }
        }, 10, 1);

        // 异步通知发送
        add_action('xzd_async_send_notification', function (array $args) {
            if (class_exists(\XZD\AI\V5\Core\NotificationThrottle::class)) {
                \XZD\AI\V5\Core\NotificationThrottle::doSend(
                    $args['subject'] ?? '',
                    $args['message'] ?? ''
                );
            }
        }, 10, 1);

        // 4c. 队列分派 Cron — 队列深时加速清空
        add_action('xzd_queue_dispatch', function () {
            if (!class_exists(\XZD\AI\V5\Tools\QueueManager::class)) return;
            $dispatched = \XZD\AI\V5\Tools\QueueManager::dispatch(0); // 0 = 动态批次（深度>50时10条，>20时5条）
            if ($dispatched > 0) {
                \XZD\AI\V5\Core\Logger::info('queue_dispatch', "分派 {$dispatched} 个Worker任务");
            }
        });
        // 统一 cron 注册（每10分钟检查一次）
        add_action('init', function () {
            if (get_transient('xzd_cron_checked')) return;
            set_transient('xzd_cron_checked', 1, 600);

            if (!wp_next_scheduled('xzd_queue_dispatch')) {
                wp_schedule_event(time() + 30, 'every_minute', 'xzd_queue_dispatch');
            }
            if (!wp_next_scheduled('xzd_heartbeat_tick')) {
                wp_schedule_event(time() + 60, 'every_minute', 'xzd_heartbeat_tick');
            }
            if (!wp_next_scheduled('xzd_queue_check')) {
                wp_schedule_event(time() + 120, 'every_5_minutes', 'xzd_queue_check');
            }
            if (!wp_next_scheduled('xzd_ai_schedule_write_check')) {
                wp_schedule_event(time() + 120, 'every_30_minutes', 'xzd_ai_schedule_write_check');
            }
            if (!wp_next_scheduled('xzd_ai_daily_event_check')) {
                wp_schedule_event(time() + 180, 'every_30_minutes', 'xzd_ai_daily_event_check');
            }
            // schedule_scan 由用户配置控制
            $triggers = Config::instance()->get('pipeline.triggers', []);
            if (in_array('schedule', $triggers, true)) {
                if (!wp_next_scheduled('xzd_ai_schedule_scan')) {
                    $scanInterval = Config::instance()->int('pipeline.scan_interval', 10);
                    $scanSchedule = self::getScanSchedule($scanInterval);
                    wp_schedule_event(time() + 60, $scanSchedule, 'xzd_ai_schedule_scan');
                }
            } else {
                $ts = wp_next_scheduled('xzd_ai_schedule_scan');
                if ($ts) wp_unschedule_event($ts, 'xzd_ai_schedule_scan');
            }
        });

        // 5. 定时清理过期批量任务
        add_action('init', function () {
            if (get_transient('xzd_cron_daily_checked')) return;
            set_transient('xzd_cron_daily_checked', 1, 600);

            if (!wp_next_scheduled('xzd_ai_cleanup_batches')) {
                wp_schedule_event(time() + DAY_IN_SECONDS, 'daily', 'xzd_ai_cleanup_batches');
            }
            if (!wp_next_scheduled('xzd_ai_daily_cleanup')) {
                $hour = Config::instance()->int('cleanup.hour', 3);
                $first = strtotime(date('Y-m-d') . " {$hour}:00:00");
                if ($first <= time()) $first += DAY_IN_SECONDS;
                wp_schedule_event($first, 'daily', 'xzd_ai_daily_cleanup');
            }
            // V3.2: 修复学习时间配置不生效 — 每次init都校验cron是否匹配用户设置
            $learnHour = Config::instance()->int('learning.schedule_hour', 3);
            $expectedNext = strtotime(date('Y-m-d') . " {$learnHour}:00:00");
            if ($expectedNext <= time()) $expectedNext += DAY_IN_SECONDS;
            $existingNext = wp_next_scheduled('xzd_ai_nightly_learn');
            if (!$existingNext || abs($existingNext - $expectedNext) > 1800) {
                if ($existingNext) wp_clear_scheduled_hook('xzd_ai_nightly_learn');
                wp_schedule_event($expectedNext, 'daily', 'xzd_ai_nightly_learn');
            }
            if (!wp_next_scheduled('xzd_license_daily_verify')) {
                $t = strtotime(date('Y-m-d') . ' 02:57:00');
                if ($t <= time()) $t += DAY_IN_SECONDS;
                wp_schedule_event($t, 'daily', 'xzd_license_daily_verify');
            }
            if (!wp_next_scheduled('xzd_license_notice_refresh')) {
                wp_schedule_event(time() + 3600, 'twicedaily', 'xzd_license_notice_refresh');
            }
            // 标签治理 — 每周日 03:30
            if (!wp_next_scheduled('xzd_ai_tag_governance_scan')) {
                $nextSun = strtotime('next Sunday 03:30:00');
                wp_schedule_event($nextSun, 'weekly', 'xzd_ai_tag_governance_scan');
            }
        });
        add_action('xzd_ai_cleanup_batches', function () {
            (new BatchPipeline())->cleanup();
        });

        // 每日定时清理旧日志（默认保留7天）
        add_action('xzd_ai_daily_cleanup', function () {
            $intervalDays = Config::instance()->int('cleanup.interval_days', 1);
            $last = get_transient('xzd_last_cleanup_date');
            $today = date('Y-m-d');
            if ($last === $today) return;
            if ($last && (strtotime($today) - strtotime($last)) < $intervalDays * DAY_IN_SECONDS) return;
            set_transient('xzd_last_cleanup_date', $today, MONTH_IN_SECONDS);

            // 清理7天前的操作日志和费用日志
            global $wpdb;
            $retainDays = Config::instance()->int('cleanup.log_retain_days', 7);
            $cutoff = date('Y-m-d H:i:s', strtotime("-{$retainDays} days"));
            $opTable = $wpdb->prefix . 'xzd_ai_operation_log';
            $costTable = $wpdb->prefix . 'xzd_ai_cost_log';
            $deletedOp = $wpdb->query($wpdb->prepare("DELETE FROM {$opTable} WHERE created_at < %s", $cutoff));
            $deletedCost = $wpdb->query($wpdb->prepare("DELETE FROM {$costTable} WHERE created_at < %s", $cutoff));
            if ($deletedOp > 0 || $deletedCost > 0) {
                Logger::info('cleanup', "清理旧日志: 操作日志{$deletedOp}条, 费用日志{$deletedCost}条");
            }

            // 清理30天前的通知限流计数（防止 wp_options 表膨胀）
            if (class_exists(\XZD\AI\V5\Core\NotificationThrottle::class)) {
                $deletedNotify = \XZD\AI\V5\Core\NotificationThrottle::cleanExpired();
                if ($deletedNotify > 0) {
                    Logger::info('cleanup', "清理过期通知计数: {$deletedNotify}条");
                }
            }
        });
        // 知识库自动学习 — 仅旗舰版
        add_action('xzd_ai_nightly_learn', function () {
            if (\XZD\AI\V5\Core\LicenseService::instance()->getLevel() !== 'ultimate') {
                return;
            }
            if (class_exists(\XZD\AI\V5\Tools\LearningEngine::class)) {
                \XZD\AI\V5\Tools\LearningEngine::instance()->run('incremental');
                if (date('N') == 7) {
                    \XZD\AI\V5\Tools\LearningEngine::instance()->cleanupExpired();
                }
            }
        });

        // 标签治理 — 每周日
        add_action('xzd_ai_tag_governance_scan', function () {
            if (class_exists(\XZD\AI\V5\SEO\TagGovernance::class)) {
                (new \XZD\AI\V5\SEO\TagGovernance())->runHealthCheck();
            }
        });

        // 死链异步扫描
        add_action('xzd_deadlink_scan_batch', function (array $args = []) {
            if (class_exists(\XZD\AI\V5\SEO\DeadLinkMonitor::class)) {
                (new \XZD\AI\V5\SEO\DeadLinkMonitor())->scanBatch(
                    (int)($args['offset'] ?? 0), (int)($args['batch'] ?? 10)
                );
            }
        });

        // 异步学习钩子 — 仅旗舰版
        add_action('xzd_ai_learning_run', function (array $args = []) {
            if (\XZD\AI\V5\Core\LicenseService::instance()->getLevel() !== 'ultimate') {
                return;
            }
            $mode = is_array($args) ? ($args['mode'] ?? 'incremental') : 'incremental';
            if (class_exists(\XZD\AI\V5\Tools\LearningEngine::class)) {
                $engine = \XZD\AI\V5\Tools\LearningEngine::instance();

                // 学习前更新种子数据、站点结构、FAQ和消息模板
                $engine->reseedKnowledgeBase();
                $engine->learnSiteStructure();
                $engine->generateSiteFAQ();
                if ($mode === 'incremental') {
                    $engine->generateProactiveTemplates();
                }

                // 主学习流程
                $engine->run($mode);
                if ($mode === 'incremental' && date('N') == 7) {
                    $engine->cleanupExpired();
                }
            }
        });

        // 友链互审监控 — 专业版+
        add_action('xzd_ai_friend_link_monitor', function () {
            if (\XZD\AI\V5\Core\LicenseService::instance()->getLevel() === 'free') return;
            if (!Config::instance()->bool('friend_link.monitor_enabled', false)) return;
            if (class_exists(\XZD\AI\V5\Tools\FriendLinkManager::class)) {
                \XZD\AI\V5\Tools\FriendLinkManager::monitorAll();
            }
        });
        // 仅在开关开启时注册 cron
        add_action('init', function () {
            $monitorEnabled = Config::instance()->bool('friend_link.monitor_enabled', false);
            $hasCron = (bool)wp_next_scheduled('xzd_ai_friend_link_monitor');
            if ($monitorEnabled && !$hasCron) {
                $monitorHour = Config::instance()->int('friend_link.monitor_hour', 2);
                $monitorFirst = strtotime(date('Y-m-d') . " {$monitorHour}:00:00");
                if ($monitorFirst <= time()) $monitorFirst += DAY_IN_SECONDS;
                wp_schedule_event($monitorFirst, 'daily', 'xzd_ai_friend_link_monitor');
            } elseif (!$monitorEnabled && $hasCron) {
                $ts = wp_next_scheduled('xzd_ai_friend_link_monitor');
                if ($ts) wp_unschedule_event($ts, 'xzd_ai_friend_link_monitor');
            }
        });

        add_action('xzd_license_daily_verify', function () {
            \XZD\AI\V5\Core\LicenseService::instance()->dailyVerify();
        });

        add_action('xzd_license_notice_refresh', function () {
            \XZD\AI\V5\Core\LicenseService::instance()->refreshNoticeAndSync();
        });

        // 定时调度检查 — 专业版+
        add_action('xzd_ai_schedule_write_check', function () {
            if (\XZD\AI\V5\Core\LicenseService::instance()->getLevel() === 'free') return;
            if (!class_exists(RuleEngine::class)) return;
            $now = wp_date('H:i');
            $context = [
                'trigger'             => 'cron',
                'current_hour_minute' => $now,
                'current_hour'        => (int)wp_date('H'),
                'current_minute'      => (int)wp_date('i'),
            ];
            RuleEngine::instance()->evaluate('xzd_ai_schedule_write', $context);
            // daily_cron_3am 每天评估一次
            $today = date('Y-m-d');
            if (!get_transient("xzd_daily_cron_ticked_{$today}")) {
                set_transient("xzd_daily_cron_ticked_{$today}", 1, DAY_IN_SECONDS * 2);
                RuleEngine::instance()->evaluate('daily_cron_3am', $context);
            }
        });

        // 每日事件检查
        add_action('xzd_ai_daily_event_check', function () {
            \XZD\AI\V5\Tools\EventTrigger::checkDailyEvents();
        });

        // 6. Action Scheduler 安全配置 + 健康检查
        $this->configureActionScheduler();
        // 早于心跳tick执行，避免重复
        add_action('init', function () {
            self::ensureActionSchedulerReady();
        }, 4);

        // 队列轮询处理器
        add_action('xzd_queue_check', function () {
            // pipeline 类型重新入队走 AS 异步；rule_evaluate 同步执行
            $batch = \XZD\AI\V5\Core\PendingQueue::drain(10);
            if (empty($batch)) return;

            foreach ($batch as $item) {
                if ($item['type'] === 'pipeline') {
                    $postId = (int)($item['post_id'] ?? 0);
                    $event  = $item['event'] ?? 'publish_post';
                    if (!$postId) continue;
                    \XZD\AI\V5\Core\PendingQueue::addPipeline($postId, $event);
                } elseif ($item['type'] === 'rule_evaluate') {
                    $event      = $item['event'] ?? '';
                    $contextKey = $item['context_key'] ?? '';
                    if (!$event || !$contextKey) continue;
                    $context = get_transient($contextKey);
                    if (!$context) continue;
                    delete_transient($contextKey);
                    try {
                        \XZD\AI\V5\Tools\RuleEngine::instance()->evaluate($event, $context);
                    } catch (\Throwable $e) {
                        \XZD\AI\V5\Core\Logger::error('queue_rule', "队列规则评估异常: {$e->getMessage()}");
                    }
                }
            }
        });

        // 心跳系统 — 每30秒检查定时任务是否逾期
        add_action('xzd_heartbeat_tick', function () {
            \XZD\AI\V5\Core\Heartbeat::tick();
        });

        // 轻量异步Runner入口，绕过AS直接处理积压的Worker action
        add_action('init', function () {
            // 支持批量token合并请求
            $tokens = [];

            if (isset($_GET['xzd_async_batch'])) {
                $raw = sanitize_text_field($_GET['xzd_async_batch']);
                $tokens = array_filter(explode(',', $raw));
            } elseif (isset($_GET['xzd_async'])) {
                $tokens = [sanitize_text_field($_GET['xzd_async'])];
            } else {
                return;
            }

            // 验证所有token
            $validTokens = [];
            foreach ($tokens as $token) {
                if (get_transient("xzd_async_{$token}")) {
                    delete_transient("xzd_async_{$token}");
                    $validTokens[] = $token;
                }
            }
            if (empty($validTokens)) return;

            // 安全限制：仅本地回环或管理员可触发
            $remoteIp = $_SERVER['REMOTE_ADDR'] ?? '';
            $isLoopback = in_array($remoteIp, ['127.0.0.1', '::1'], true)
                || ($remoteIp === $_SERVER['SERVER_ADDR'] ?? '');
            if (!$isLoopback && !current_user_can('manage_options')) {
                wp_send_json(['ok' => false, 'error' => 'forbidden'], 403);
            }

            global $wpdb;
            $tableActions = $wpdb->prefix . 'actionscheduler_actions';
            $processed = 0;

            // 批量处理：每个有效token处理一个pending action
            foreach ($validTokens as $token) {
                $row = $wpdb->get_row(
                    "SELECT action_id, args FROM {$tableActions}
                     WHERE hook = 'xzd_worker_process'
                     AND status = 'pending'
                     AND claim_id = 0
                     ORDER BY scheduled_date_gmt ASC
                     LIMIT 1"
                );

                if (!$row) break;

                $args = json_decode($row->args, true);
                if (!is_array($args)) continue;
                $actionId = (int) $row->action_id;
                $postId   = (int) ($args[0]['post_id'] ?? 0);

                // 原子 UPDATE 模拟 claim，防止与 AS Runner 重复处理
                $claimed = $wpdb->query($wpdb->prepare(
                    "UPDATE {$tableActions}
                     SET status = 'in-progress', last_attempt_gmt = %s, claim_id = -1
                     WHERE action_id = %d AND status = 'pending' AND claim_id = 0",
                    current_time('mysql', true), $actionId
                ));
                if (!$claimed) continue;

                // 清理残留的 Worker 锁和 running 状态
                if ($postId) {
                    $pipelineMeta = get_post_meta($postId, '_xzd_ai_pipeline_status', true);
                    if (is_array($pipelineMeta) && ($pipelineMeta['status'] ?? '') === 'running') {
                        $startTs = \XZD\AI\V5\Tools\PipelineEngine::parseLocalTime(
                            $pipelineMeta['started_at'] ?? $pipelineMeta['created_at'] ?? ''
                        );
                        if (!$startTs || (time() - $startTs) > 120) {
                            delete_post_meta($postId, '_xzd_ai_pipeline_status');
                            delete_post_meta($postId, '_xzd_ai_pipeline_retries');
                            delete_transient('xzd_lock_' . md5("xzd_worker_post_{$postId}"));
                        }
                    }
                }

                try {
                    \XZD\AI\V5\Tools\PostWorker::process($args[0]);
                    $wpdb->update($tableActions, [
                        'status'           => 'complete',
                        'last_attempt_gmt' => current_time('mysql', true),
                    ], ['action_id' => $actionId]);
                } catch (\Throwable $e) {
                    $wpdb->update($tableActions, [
                        'status'           => 'failed',
                        'last_attempt_gmt' => current_time('mysql', true),
                    ], ['action_id' => $actionId]);
                }
                $processed++;
            }

            wp_send_json(['ok' => true, 'processed' => $processed, 'time' => time()]);
        }, 999);

        // AS Runner 保活和卡死检测
        add_action('init', function () {
            if (!function_exists('as_has_scheduled_action') || !function_exists('as_schedule_single_action')) return;

            // 每2分钟最多检查一次
            $asCheckKey = 'xzd_as_health_last_check';
            $lastCheck = get_transient($asCheckKey);
            if ($lastCheck && (time() - $lastCheck) < 120) return;
            set_transient($asCheckKey, time(), 180);

            global $wpdb;
            $table = $wpdb->prefix . 'actionscheduler_actions';

            // 查找pending的Runner
            $runner = $wpdb->get_row(
                "SELECT action_id, status, scheduled_date_gmt, last_attempt_gmt
                 FROM {$table}
                 WHERE hook = 'action_scheduler_run_queue'
                 AND status = 'pending'
                 ORDER BY action_id DESC LIMIT 1"
            );

            if (!$runner) {
                // 没有Runner → 创建
                as_schedule_single_action(time() + 5, 'action_scheduler_run_queue');
                \XZD\AI\V5\Core\Logger::info('as_health', 'AS Runner缺失，已补调');
                return;
            }

            // 检查是否卡死：从未执行过且调度时间超过5分钟
            $neverAttempted = ($runner->last_attempt_gmt === '0000-00-00 00:00:00' || !$runner->last_attempt_gmt);
            $scheduledTs = strtotime($runner->scheduled_date_gmt);
            // scheduled_date_gmt 可能为零值（0000-00-00），两字段均为零值则判定为卡死
            if ($scheduledTs && $scheduledTs > 0) {
                $stuckMinutes = (int) ((time() - $scheduledTs) / 60);
            } else {
                $stuckMinutes = $neverAttempted ? 999 : 0;
            }

            if ($neverAttempted && $stuckMinutes > 5) {
                self::rebuildStuckRunner($wpdb, $table, $runner, $stuckMinutes);
            }

            // 检测执行过一次后卡死的盲区
            $stuckAfterOneRun = ($runner->status === 'pending'
                && $runner->last_attempt_gmt !== '0000-00-00 00:00:00'
                && !empty($runner->last_attempt_gmt)
                && strtotime($runner->last_attempt_gmt) < time() - 600);
            if ($stuckAfterOneRun) {
                $stuckMinutes2 = (int) round((time() - strtotime($runner->last_attempt_gmt)) / 60);
                self::rebuildStuckRunner($wpdb, $table, $runner, $stuckMinutes2);
            }
        });

        // tick 全部走异步
        // 心跳 REST 端点
        add_action('rest_api_init', function () {
            // GET /xzdbk-ai/v1/heartbeat — 状态查询
            register_rest_route('xzdbk-ai/v1', '/heartbeat', [
                'methods'             => 'GET',
                'callback'            => function () {
                    return new \WP_REST_Response(\XZD\AI\V5\Core\Heartbeat::getStatus(), 200);
                },
                'permission_callback' => function () {
                    return current_user_can('edit_posts');
                },
            ]);
            // 异步 Pipeline Worker 端点
            register_rest_route('xzdbk-ai/v1', '/pipeline/process-pending', [
                'methods'             => 'POST',
                'callback'            => function () {
                    $start = microtime(true);
                    $batch = \XZD\AI\V5\Core\PendingQueue::drain(1);
                    if (empty($batch)) {
                        return new \WP_REST_Response(['ok' => true, 'processed' => false, 'msg' => 'no items'], 200);
                    }
                    $item = $batch[0];
                    try {
                        if (($item['type'] ?? '') === 'pipeline') {
                            (new \XZD\AI\V5\Tools\PipelineEngine())->run(
                                (int)($item['post_id'] ?? 0),
                                $item['event'] ?? 'publish_post'
                            );
                        }
                        $elapsed = round((microtime(true) - $start) * 1000);
                        return new \WP_REST_Response(['ok' => true, 'processed' => true, 'post_id' => $item['post_id'] ?? 0, 'elapsed_ms' => $elapsed], 200);
                    } catch (\Throwable $e) {
                        return new \WP_REST_Response(['ok' => false, 'error' => $e->getMessage()], 500);
                    }
                },
                'permission_callback' => function () {
                    return current_user_can('edit_posts');
                },
            ]);

            // POST /xzdbk-ai/v1/heartbeat/tick — 强制触发
            register_rest_route('xzdbk-ai/v1', '/heartbeat/tick', [
                'methods'             => 'POST',
                'callback'            => function () {
                    $result = \XZD\AI\V5\Core\Heartbeat::tick();
                    return new \WP_REST_Response($result, $result['ok'] ? 200 : 500);
                },
                'permission_callback' => function () {
                    return true; // self-ping 无需认证
                },
            ]);
        });

        // FULLTEXT 降级通知
        add_action('admin_notices', function () {
            if (get_transient('xzd_ai_fulltext_degraded')) {
                echo '<div class="notice notice-warning is-dismissible"><p><strong>[小栈AI]</strong> 知识库全文索引（FULLTEXT）创建失败，已自动降级为普通索引。大文本搜索可能较慢，建议联系主机商开启 MySQL FULLTEXT 或升级 MySQL 至 5.6+。</p></div>';
            }
        });

        // 7. FAQ文章样式 — 自动在文章页加载
        add_action('wp_enqueue_scripts', function () {
            if (is_singular('post')) {
                $cssFile = XZD_AI_DIR . 'assets/standalone/faq-accordion.css';
                if (file_exists($cssFile)) {
                    wp_enqueue_style('xzd-faq-accordion', XZD_AI_URL . 'assets/standalone/faq-accordion.css', [], XZD_AI_VERSION);
                }
            }
        });

        // 8. 品牌标识 — 控制台版权信息
        add_action('wp_head', [$this, 'printConsoleBrand']);
        add_action('admin_head', [$this, 'printConsoleBrand']);
    }

    /**
     * 在浏览器控制台输出版权信息
     */
    public function printConsoleBrand(): void
    {
        $plugin_name = '小栈AI';
        $version     = XZD_AI_VERSION;
        $url         = 'www.xzdbk.com';

        echo "<script>\n";
        echo "console.log('%c{$plugin_name} v{$version} %c{$url}%c',\n";
        echo "  'background:#1f2937;color:#fbbf24;padding:8px 4px 8px 12px;border-radius:6px 0 0 6px;font-size:13px;font-weight:bold;',\n";
        echo "  'background:#fbbf24;color:#1f2937;padding:8px 12px;border-radius:0 6px 6px 0;font-size:13px;font-weight:bold;',\n";
        echo "  '');\n";
        echo "</script>\n";
    }

    /**
     * 插件激活 — 建表 + 初始设置
     */
    public function activate(): void
    {
        // 建表
        $schema = new Schema();
        $schema->migrate();

        // 记录首次安装日期
        if (!get_option('xzd_plugin_install_date')) {
            add_option('xzd_plugin_install_date', current_time('mysql'), '', 'no');
        }

        // 版本升级时清理旧状态
        $existingVersion = Config::instance()->get('version');
        if ($existingVersion && $existingVersion !== XZD_AI_VERSION) {
            $this->cleanupOnReactivation($existingVersion);
        }

        // 设置默认选项
        $config = Config::instance();
        if (!$config->get('version')) {
            $config->set('version', XZD_AI_VERSION);
            $config->set('installed_at', current_time('mysql'));

            // 默认速率限制
            $config->set('rate_limit.guest.daily', 20);
            $config->set('rate_limit.guest.per_minute', 5);
            $config->set('rate_limit.guest.interval', 3);
            $config->set('rate_limit.user.daily', 50);
            $config->set('rate_limit.user.per_minute', 10);
            $config->set('rate_limit.user.interval', 2);
            $config->set('rate_limit.vip.daily', 200);
            $config->set('rate_limit.vip.per_minute', 20);
            $config->set('rate_limit.vip.interval', 1);
            $config->set('rate_limit.admin.unlimited', true);
            $config->set('cost.monthly_budget', 0);
            $config->set('cost.over_budget_action', 'warn');

            // 用户角色默认禁用内容工具
            $config->set('tools.blocked_tools_by_role', [
                'guest' => [],
                'user'  => [
                    'generate_tags', 'generate_seo', 'generate_summary',
                    'polish_content', 'translate_content', 'rewrite_content', 'expand_content',
                    'generate_cover', 'generate_alt', 'build_autolink',
                    'write_article', 'score_quality', 'check_duplicate',
                ],
                'vip'   => [],
                'admin' => [],
            ]);

            $config->save();

            // 种子预设自动化规则
            RuleEngine::instance()->seedPresets();

            // 首次安装时收集网站画像 + 种子同义词
            $this->seedKnowledgeBase();
            if (class_exists(\XZD\AI\V5\Tools\ContentManager::class)) {
                \XZD\AI\V5\Tools\ContentManager::seedBuiltinSynonyms();
            }
        }
    }

    /**
     * 种子知识库 — 首次安装时自动生成网站画像
     */
    private function seedKnowledgeBase(): void
    {
        global $wpdb;
        $table = $wpdb->prefix . 'xzd_ai_knowledge';

        // 确保表存在
        $exists = $wpdb->get_var("SHOW TABLES LIKE '{$table}'") === $table;
        if (!$exists) return;

        $now = current_time('mysql');

        // 1. 网站基本信息
        $siteName = get_bloginfo('name');
        $siteDesc = get_bloginfo('description');
        $siteUrl  = home_url();
        $wpdb->insert($table, [
            'title'       => "网站概况: {$siteName}",
            'content'     => "网站名称: {$siteName}\n网站描述: {$siteDesc}\n网站地址: {$siteUrl}\nWordPress版本: " . get_bloginfo('version') . "\n语言: " . get_locale() . "\n时区: " . wp_timezone_string(),
            'source_type' => 'system',
            'status'      => 'active',
            'created_at'  => $now,
            'updated_at'  => $now,
        ]);

        // 2. 主题信息
        $theme = wp_get_theme();
        $wpdb->insert($table, [
            'title'       => "当前主题: {$theme->get('Name')}",
            'content'     => "主题名称: {$theme->get('Name')}\n版本: {$theme->get('Version')}\n作者: {$theme->get('Author')}\n描述: {$theme->get('Description')}",
            'source_type' => 'system',
            'status'      => 'active',
            'created_at'  => $now,
            'updated_at'  => $now,
        ]);

        // 3. 内容统计
        $postCount = wp_count_posts();
        $catCount  = wp_count_terms('category');
        $tagCount  = wp_count_terms('post_tag');
        $userCount = count_users();
        $wpdb->insert($table, [
            'title'       => '网站内容统计',
            'content'     => "已发布文章: " . ($postCount->publish ?? 0) . " 篇\n草稿: " . ($postCount->draft ?? 0) . " 篇\n分类数: {$catCount}\n标签数: {$tagCount}\n用户数: " . ($userCount['total_users'] ?? 0) . "\n管理员数: " . ($userCount['avail_roles']['administrator'] ?? 0),
            'source_type' => 'system',
            'status'      => 'active',
            'created_at'  => $now,
            'updated_at'  => $now,
        ]);

        // 4. 主要分类及文章量
        $categories = get_categories(['orderby' => 'count', 'order' => 'DESC', 'number' => 10]);
        $catInfo = '';
        foreach ($categories as $cat) {
            $catInfo .= "• {$cat->name} (ID:{$cat->term_id}): {$cat->count} 篇文章\n";
        }
        if ($catInfo) {
            $wpdb->insert($table, [
                'title'       => '网站分类分布',
                'content'     => "主要分类及文章数量:\n{$catInfo}",
                'source_type' => 'analysis',
                'status'      => 'active',
                'created_at'  => $now,
                'updated_at'  => $now,
            ]);
        }

        // 5. 已安装的活跃插件
        $activePlugins = get_option('active_plugins', []);
        $pluginNames = [];
        foreach (array_slice($activePlugins, 0, 20) as $plugin) {
            $data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);
            $pluginNames[] = ($data['Name'] ?? basename($plugin)) . ' v' . ($data['Version'] ?? '?');
        }
        $wpdb->insert($table, [
            'title'       => '已安装插件列表',
            'content'     => "活跃插件 (" . count($activePlugins) . " 个):\n" . implode("\n", array_map(fn($n) => "• {$n}", $pluginNames)),
            'source_type' => 'system',
            'status'      => 'active',
            'created_at'  => $now,
            'updated_at'  => $now,
        ]);

        Logger::info('knowledge_base_seeded', '知识库已自动填充网站画像（5条记录）');
    }

    /**
     * 插件停用 — 清理定时任务
     */
    public function deactivate(): void
    {
        wp_clear_scheduled_hook('xzd_ai_cron_schedule');
        wp_clear_scheduled_hook('xzd_ai_health_check');
        wp_clear_scheduled_hook('xzd_ai_process_batch');
        wp_clear_scheduled_hook('xzd_ai_cleanup_batches');
        wp_clear_scheduled_hook('xzd_ai_daily_cleanup');
        wp_clear_scheduled_hook('xzd_ai_schedule_scan');
        wp_clear_scheduled_hook('xzd_ai_friend_link_monitor');
        wp_clear_scheduled_hook('xzd_license_daily_verify');
        wp_clear_scheduled_hook('xzd_license_notice_refresh');
        wp_clear_scheduled_hook('xzd_ai_nightly_learn');
        wp_clear_scheduled_hook('xzd_ai_tag_governance_scan');
        wp_clear_scheduled_hook('xzd_ai_learning_run');
        wp_clear_scheduled_hook('xzd_ai_schedule_write_check');
        wp_clear_scheduled_hook('xzd_ai_daily_event_check');
        wp_clear_scheduled_hook('xzd_queue_dispatch');
        wp_clear_scheduled_hook('xzd_queue_check');
        wp_clear_scheduled_hook('xzd_heartbeat_tick');
    }

    /**
     * 插件升级时清理旧状态，防止队列和流水线卡住
     */
    private function cleanupOnReactivation(string $oldVersion): void
    {
        Logger::info('plugin_upgrade', "插件升级: {$oldVersion} → " . XZD_AI_VERSION . "，清理旧状态");

        // 1. 重置扫描状态
        delete_option('xzd_scan_state');

        // 2. 清空队列
        delete_option('xzd_worker_queue');
        delete_option('xzd_pending_queue');

        // 3. 取消所有 pending 的 xzd_worker_process
        if (function_exists('as_unschedule_all_actions')) {
            as_unschedule_all_actions('xzd_worker_process');
            Logger::info('plugin_upgrade', '已取消所有 AS pending actions');
        }

        // 4. 重置卡在 running/pending 状态的流水线
        global $wpdb;
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s
             AND (meta_value LIKE %s OR meta_value LIKE %s)",
            '_xzd_ai_pipeline_status',
            '%"status";s:7:"running"%',
            '%"status";s:7:"pending"%'
        ));

        // 5. 清除失败冷却计数器
        $wpdb->query(
            "DELETE FROM {$wpdb->postmeta} WHERE meta_key = '_xzd_ai_failed_cycles'"
        );

        // 6. 清除断路器冷却状态
        delete_transient('xzd_ai_circuit_breaker');
        delete_transient('xzd_ai_global_cooldown');

        Logger::info('plugin_upgrade', "升级清理完成 ({$oldVersion} → " . XZD_AI_VERSION . ')');
    }

    /**
     * 插件卸载 — 删除所有数据库表和选项
     */
    public static function uninstall(): void
    {
        require_once XZD_AI_DIR . 'src/Database/Schema.php';
        (new \XZD\AI\V5\Database\Schema())->uninstall();
    }

    /**
     * Action Scheduler 安全配置
     *
     * 按主机等级自动调整并发参数。
     */
    private function configureActionScheduler(): void
    {
        // 主机等级检测
        $hostTier = $this->detectHostTier();

        // 按主机等级调整超时时间
        add_filter('action_scheduler_queue_runner_time_limit', function ($timeLimit, $hook = null) use ($hostTier) {
            if (str_starts_with($hook ?? '', 'xzd_ai_')) {
                return match ($hostTier) {
                    'dedicated' => 120,
                    'vps'       => 60,
                    default     => 45,
                };
            }
            return $timeLimit;
        }, 10, 2);

        // 大规模站点禁用异步 runner
        $postCount = get_transient('xzd_post_count');
        if ($postCount === false) {
            $postCount = wp_count_posts()->publish ?? 0;
            set_transient('xzd_post_count', $postCount, HOUR_IN_SECONDS);
        }
        if ($postCount > 5000) {
            add_filter('action_scheduler_allow_async_request_runner', '__return_false');
        }

        // 动态并发 — 手动模式使用配置值，自动模式按负载计算
        add_filter('action_scheduler_queue_runner_concurrent_batches', function ($concurrent) {
            $userMax = Config::instance()->int('worker.max_concurrent', 0);

            // 手动模式：用户设几个就是几个
            if ($userMax > 0) {
                return (int) $userMax;
            }

            // 自动模式：根据服务器实时负载动态调整
            return self::calcDynamicConcurrency();
        });
    }

    /**
     * 确保 Action Scheduler 正常运行
     *
     * 每30分钟检查一次：建表、补调 runner、清理僵尸任务。
     */
    public static function ensureActionSchedulerReady(): void
    {
        $checkKey = 'xzd_as_health_check';
        if (get_transient($checkKey)) return;
        set_transient($checkKey, 1, 1800);

        if (!class_exists('ActionScheduler_StoreSchema')) return;

        global $wpdb;
        $table = $wpdb->prefix . 'actionscheduler_actions';

        // 1. 确保表存在
        $tableName = $wpdb->prefix . 'actionscheduler_actions';
        $found = $wpdb->get_var(
            "SHOW TABLES LIKE '{$wpdb->prefix}actionscheduler_actions'"
        );
        if ($found !== $tableName) {
            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
            (new \ActionScheduler_StoreSchema())->register_tables(true);
            (new \ActionScheduler_LoggerSchema())->register_tables(true);
            Logger::warning('as_health', 'AS表缺失，已自动创建');
            delete_transient($checkKey);
        }

        // 2. 确保runner已调度
        if (function_exists('as_has_scheduled_action') && function_exists('as_schedule_single_action')) {
            if (!as_has_scheduled_action('action_scheduler_run_queue')) {
                as_schedule_single_action(time() + 10, 'action_scheduler_run_queue');
                Logger::warning('as_health', 'AS runner缺失，已紧急补调');
            }
        }

        // 3. 僵尸任务清理
        if (class_exists('\ActionScheduler_Store')) {
            try {
                global $wpdb;
                $tableActions = $wpdb->prefix . 'actionscheduler_actions';
                $staleCount = (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(*) FROM {$tableActions}
                     WHERE hook = %s AND status = 'pending'",
                    'xzd_worker_process'
                ));

                if ($staleCount > 20) {
                    // 检查最早的pending任务是否逾期超1小时
                    $oldest = as_get_scheduled_actions([
                        'hook'     => 'xzd_worker_process',
                        'status'   => \ActionScheduler_Store::STATUS_PENDING,
                        'group'    => 'xzd_worker',
                        'per_page' => 1,
                        'orderby'  => 'schedule',
                        'order'    => 'ASC',
                    ]);
                    if (!empty($oldest) && isset($oldest[0]) && $oldest[0]->get_schedule() && $oldest[0]->get_schedule()->get_date()) {
                        $oldestDate = null; if ($oldest[0]->get_schedule() && $oldest[0]->get_schedule()->get_date()) { $oldestDate = $oldest[0]->get_schedule()->get_date()->getTimestamp(); }
                        if (time() - $oldestDate > HOUR_IN_SECONDS) {
                            // 全部逾期 → 清理重建
                            $wpdb->query($wpdb->prepare(
                                "DELETE FROM {$wpdb->prefix}actionscheduler_actions WHERE hook = %s AND status = %d",
                                'xzd_worker_process', 0
                            ));
                            Logger::warning('as_health', "AS僵尸任务堆积{$staleCount}个（全部逾期>1h），已清理。下次tick将重建。");
                            // 清理后下次 tick 将重建
                            delete_transient(\XZD\AI\V5\Core\Heartbeat::LAST_TICK_KEY);
                        }
                    }
                }
            } catch (\Throwable $e) { /* 静默 */ }
        }
    }

    /**
     * 重建卡死的 AS Runner
     */
    private static function rebuildStuckRunner($wpdb, string $table, object $runner, int $stuckMinutes): void
    {
        $wpdb->delete($table, ['action_id' => $runner->action_id]);
        as_schedule_single_action(time() + 5, 'action_scheduler_run_queue');

        // 同时清理僵尸claims
        $claimsTable = $wpdb->prefix . 'actionscheduler_claims';
        $cutoff = date('Y-m-d H:i:s', time() - 600);
        $deleted = $wpdb->query($wpdb->prepare(
            "DELETE FROM {$claimsTable} WHERE date_created_gmt < %s", $cutoff
        ));

        \XZD\AI\V5\Core\Logger::info('as_health',
            "AS Runner卡死{$stuckMinutes}分钟(状态=" . ($runner->status ?? 'pending') . ")，已重建并手动触发。清理claims={$deleted}"
        );
    }

    /**
     * 重调度学习 Cron
     */
    public static function rescheduleLearningCron(int $hour): void
    {
        wp_clear_scheduled_hook('xzd_ai_nightly_learn');
        $next = strtotime(date('Y-m-d') . " {$hour}:00:00");
        if ($next <= time()) {
            $next += DAY_IN_SECONDS;
        }
        wp_schedule_event($next, 'daily', 'xzd_ai_nightly_learn');
    }

    /**
     * 重调度扫描 Cron
     */
    public static function rescheduleScanCron(int $intervalMinutes): void
    {
        wp_clear_scheduled_hook('xzd_ai_schedule_scan');
        $schedule = self::getScanSchedule($intervalMinutes);
        wp_schedule_event(time() + 60, $schedule, 'xzd_ai_schedule_scan');
    }

    /**
     * 重调度友链监控 Cron
     */
    public static function rescheduleFriendLinkMonitorCron(int $hour): void
    {
        wp_clear_scheduled_hook('xzd_ai_friend_link_monitor');
        $next = strtotime(date('Y-m-d') . " {$hour}:00:00");
        if ($next <= time()) $next += DAY_IN_SECONDS;
        wp_schedule_event($next, 'daily', 'xzd_ai_friend_link_monitor');
    }

    /**
     * 重调度每日清理 Cron
     */
    public static function rescheduleCleanupCron(int $hour): void
    {
        wp_clear_scheduled_hook('xzd_ai_daily_cleanup');
        $next = strtotime(date('Y-m-d') . " {$hour}:00:00");
        if ($next <= time()) $next += DAY_IN_SECONDS;
        wp_schedule_event($next, 'daily', 'xzd_ai_daily_cleanup');
    }

    /**
     * 将分钟间隔映射到 Cron schedule 标识符
     */
    private static function getScanSchedule(int $minutes): string
    {
        return match (true) {
            $minutes <= 1  => 'every_minute',
            $minutes <= 5  => 'every_5_minutes',
            $minutes <= 10 => 'every_10_minutes',
            $minutes <= 15 => 'every_15_minutes',
            $minutes <= 30 => 'every_30_minutes',
            default        => 'hourly',
        };
    }

    /**
     * 获取CPU核心数调整后的负载（每核平均负载）
     *
     * @return float 每核平均负载（fallback: raw load[0]）
     */
    public static function getCpuAdjustedLoad(): float
    {
        $load = function_exists('sys_getloadavg') ? sys_getloadavg() : [0];
        static $cpuCores = null;
        if ($cpuCores === null) {
            $cpuCores = 1;
            // nproc 优先，/proc/cpuinfo 兜底
            if (function_exists('\\shell_exec') && strpos(ini_get('disable_functions'), 'shell_exec') === false) {
                $nproc = @\shell_exec('nproc 2>/dev/null');
                if ($nproc && (int)$nproc > 0) {
                    $cpuCores = (int)$nproc;
                }
            }
            // 检查 open_basedir 避免触发文件访问警告
            $hasOpenBasedir = ini_get('open_basedir') && ini_get('open_basedir') !== '';
            if ($cpuCores <= 1 && !$hasOpenBasedir && @is_readable('/proc/cpuinfo')) {
                $cpuinfo = @file_get_contents('/proc/cpuinfo');
                if ($cpuinfo) {
                    $cpuCores = max(1, substr_count($cpuinfo, 'processor'));
                }
            }
        }
        return $load[0] / max(1, $cpuCores);
    }

    /**
     * 根据服务器负载和内存自动调整 Worker 并发数
     *
     * @return int 1~4
     */
    public static function calcDynamicConcurrency(): int
    {
        $loadRatio = self::getCpuAdjustedLoad();
        $memLimit = ini_get('memory_limit');
        $limit = trim($memLimit);
        $bytes = (int)$limit;
        $last = strtolower(substr($limit, -1));
        $memBytes = match ($last) {
            'g' => $bytes * 1073741824,
            'm' => $bytes * 1048576,
            'k' => $bytes * 1024,
            default => (int)$limit ?: 128 * 1048576,
        };
        $freeMem = $memBytes - memory_get_usage(true);
        $freeMemMB = (int)($freeMem / 1048576);
        $memLimitMB = (int)($memBytes / 1048576);

        // 按空闲内存比例判断
        $freePercent = $memLimitMB > 0 ? (int)($freeMemMB * 100 / $memLimitMB) : 50;

        // 系统高负载（每核>2.0） → 降至1
        if ($loadRatio > 2.0) {
            return 1;
        }
        // 中等负载（每核>1.0）或内存紧张（<30%） → 保守2
        if ($loadRatio > 1.0 || $freePercent < 30) {
            return 2;
        }
        // 低负载 + 内存充裕（≥50%） → 全速4
        if ($freePercent >= 50) {
            return 4;
        }
        // 至少2个确保不断档
        return max(2, (int) ceil($freePercent / 25));
    }

    private function detectHostTier(): string
    {
        static $tier = null;
        if ($tier !== null) {
            return $tier;
        }

        $memoryLimit = ini_get('memory_limit');
        $memoryBytes = $this->parseMemoryLimit($memoryLimit);

        if ($memoryBytes >= 512 * 1024 * 1024) {
            $tier = 'dedicated';
        } elseif ($memoryBytes >= 256 * 1024 * 1024) {
            $tier = 'vps';
        } else {
            $tier = 'shared';
        }

        return $tier;
    }

    private function parseMemoryLimit(string $limit): int
    {
        return Config::parseMemoryLimit($limit, 128 * 1024 * 1024);
    }

    /**
     * 注册默认模块
     */
    private function registerDefaultModules(): void
    {
        // 后台管理页面
        $this->modules[] = [
            'name'     => 'admin_page',
            'class'    => AdminPage::class,
            'method'   => 'init',
            'priority' => 10,
        ];

        // 全局对话弹窗 (前/后台通用)
        $this->modules[] = [
            'name'     => 'floating_chat',
            'class'    => FloatingChat::class,
            'method'   => 'init',
            'priority' => 20,
        ];

        // 文章编辑页 AI 助手 (Gutenberg面板 + 经典编辑器Metabox)
        $this->modules[] = [
            'name'     => 'post_editor_ai',
            'class'    => PostEditorAI::class,
            'method'   => 'init',
            'priority' => 12,
        ];

        // SEO 前端输出 (title/description/keywords — 被SEO插件接管时不输出)
        $this->modules[] = [
            'name'     => 'meta_renderer',
            'class'    => MetaRenderer::class,
            'method'   => 'init',
            'priority' => 5,
        ];

        // SEO 桥接 — 检测专业SEO插件并同步AI数据
        // llms.txt
        $this->modules[] = [
            'name'     => 'llmstxt',
            'class'    => LLMSTxtGenerator::class,
            'method'   => 'init',
            'priority' => 7,
        ];

        // 文章编辑 SEO Meta Box
        $this->modules[] = [
            'name'     => 'seo_meta_box',
            'class'    => SEOMetaBox::class,
            'method'   => 'init',
            'priority' => 12,
        ];

        // 摘要前端展示 (文章页内容上方)
        $this->modules[] = [
            'name'     => 'summary_renderer',
            'class'    => SummaryRenderer::class,
            'method'   => 'init',
            'priority' => 15,
        ];

        // 标签美化 — 待TagStyleManager.php部署后启用
        // $this->modules[] = [
        //     'name'     => 'tag_style',
        //     'class'    => TagStyleManager::class,
        //     'method'   => 'init',
        //     'priority' => 16,
        // ];

        // Schema 迁移（在 admin_init 时检查）
        $this->modules[] = [
            'name'     => 'schema',
            'callback' => function () {
                add_action('init', function () {
                    (new Schema())->migrate();
                }, 5);
                add_action('admin_init', function () {
                    (new Schema())->migrate();
                });
            },
        ];

        // REST API 路由
        $this->modules[] = [
            'name'     => 'rest_router',
            'callback' => function () {
                Router::register(HealthController::class);
                Router::register(SettingsController::class);
                Router::register(AIConnectionController::class);
                Router::register(ToolController::class);
                Router::register(ChatMessageController::class);
                Router::register(LogController::class);
                Router::register(LicenseController::class);
                Router::register(PipelineController::class);
                Router::register(AutomationController::class);
                Router::register(ThemeController::class);
                Router::register(AnalyticsController::class);
                Router::register(TagStyleController::class);
                Router::register(\XZD\AI\V5\REST\Controllers\TagGovernanceController::class);
                Router::init();
            },
        ];

        // Zibll 集成模块（依赖 Zibll 主题）
        $this->modules[] = [
            'name'     => 'zibll_payment',
            'callback' => function () {
                if (get_template() === 'zibll') {
                    ZibllPaymentIntegration::register();
                }
            },
        ];
        // 前端用户中心 AI 助手已停用
        $this->modules[] = [
            'name'     => 'zibll_frontend_editor',
            'callback' => function () {
                if (get_template() === 'zibll') {
                    ZibllFrontendEditorIntegration::register();
                }
            },
            'priority' => 25,
        ];
        $this->modules[] = [
            'name'     => 'zibll_bbs',
            'callback' => function () {
                if (get_template() === 'zibll') {
                    ZibllBBSIntegration::register();
                }
            },
        ];
    }

    /**
     * 初始化所有模块
     */
    private function initModules(): void
    {
        foreach ($this->modules as $module) {
            if (isset($module['callback'])) {
                ($module['callback'])();
            } elseif (isset($module['class'], $module['method'])) {
                $class = $module['class'];
                $method = $module['method'];
                (new $class())->{$method}();
            }
        }
    }

    /**
     * 动态注册新模块
     */
    public function registerModule(array $module): self
    {
        $this->modules[] = $module;
        return $this;
    }
}
