<?php

namespace XZD\AI\V5\Tools;

use XZD\AI\V5\Core\Config;
use XZD\AI\V5\Core\Logger;
use XZD\AI\V5\Core\PendingQueue;

/**
 * WordPress 事件触发器 — 监听 WP 钩子并触发流水线/自动化规则
 *
 * 监听事件:
 *  - publish_post → 流水线 + 自动化规则
 *  - save_post → 流水线（仅 update 触发开启时）
 *  - xzd_ai_schedule_scan → 定时扫描未处理文章
 *  - user_register / wp_login / comment_post → 自动化规则
 *
 * 性能设计（V7）：
 *  - 所有处理通过 PendingQueue 轻量入队，绝不阻塞用户请求
 *  - 请求级静态缓存去重，防止 save_post 多次触发
 *  - 不再调用 wp_schedule_single_event() 或 spawn_cron()
 */
class EventTrigger
{
    private static bool $initialized = false;

    /** 请求级去重缓存（同一请求同一文章只入队一次） */
    private static array $enqueued = [];

    /**
     * 注册所有 WordPress 钩子
     */
    public static function init(): void
    {
        if (self::$initialized) {
            return;
        }
        self::$initialized = true;

        // ── 流水线触发 ──
        add_action('publish_post', [self::class, 'handlePublish'], 20, 1);
        add_action('save_post', [self::class, 'handleSave'], 20, 1);

        // ── 导入事件 ──
        add_action('import_post', [self::class, 'handleImportPost'], 20, 1);
        add_action('import_end', [self::class, 'handleImportEnd'], 20, 0);

        // ── 定时扫描 ──
        add_action('xzd_ai_schedule_scan', [self::class, 'scheduleScan']);

        // ── 自动化规则触发 ──
        add_action('user_register', [self::class, 'handleUserRegister'], 20, 1);
        add_action('wp_login', [self::class, 'handleLogin'], 20, 2);
        add_action('comment_post', [self::class, 'handleComment'], 20, 2);
        add_action('transition_comment_status', [self::class, 'handleCommentApproved'], 20, 3);
        add_action('wp_insert_link', [self::class, 'handleLinkSubmitted'], 20, 1);

        // ── 异步流水线阶段 ──
        add_action('xzd_ai_pipeline_async_stage', [PipelineEngine::class, 'executeAsyncStage'], 10, 1);

        // ── 异步处理（cron 回调） ──
        add_action(PipelineEngine::PROCESS_HOOK, [PipelineEngine::class, 'processPost'], 10, 1);
        add_action(RuleEngine::ASYNC_HOOK, [RuleEngine::class, 'evaluateFromCron'], 10, 1);
    }

    // ═══════════════════════════════════════════════
    // 文章事件
    // ═══════════════════════════════════════════════

    /**
     * 文章发布 → 调度自动化规则 + 流水线（V7：按阶段粒度协作，不再互斥）
     *
     * V7 改进：规则和流水线同时运行，各自的 per-tool fill_empty 标记
     * （_xzd_ai_processed_{toolId}）天然防止重复处理同一阶段。
     * 用户配置的规则处理标签+SEO，流水线处理封面+内链，互不冲突。
     */
    public static function handlePublish(int $postId): void
    {
        // 请求级去重：同一文章同一次请求只处理一次
        $dedupKey = "publish_{$postId}";
        if (isset(self::$enqueued[$dedupKey])) return;
        self::$enqueued[$dedupKey] = true;

        if (wp_is_post_autosave($postId) || wp_is_post_revision($postId)) {
            return;
        }

        $post = get_post($postId);
        if (!$post || $post->post_type !== 'post') {
            return;
        }

        $hasRules = class_exists(RuleEngine::class) && RuleEngine::instance()->hasEnabledRules('publish_post');

        // 检查流水线是否启用
        $triggers = Config::instance()->get('pipeline.triggers', []);
        $defaultOn = empty($triggers);
        $publishOn = $defaultOn || in_array('publish', $triggers, true);

        // V7：规则和流水线同时入队（按阶段粒度协作，per-tool fill_empty 防重）
        if ($hasRules) {
            PendingQueue::addRuleEvaluation('publish_post', [
                'post_id'      => $postId,
                'post_title'   => $post->post_title,
                'post_type'    => $post->post_type,
                'post_author'  => $post->post_author,
                'post_status'  => $post->post_status,
                'category_ids' => wp_get_post_categories($postId),
            ]);
        }

        if ($publishOn) {
            // V8：新发布文章快速通道 — 直接进入 PendingQueue，跳过 WorkerQueue 排队
            // PendingQueue 由 xzd_queue_check（每5分钟）和 Heartbeat::tick（每2分钟）消费
            PipelineEngine::recoverStaleRunning($postId);
            if (!PipelineEngine::isFullyProcessed($postId, 'publish_post')) {
                PendingQueue::addPipeline($postId, 'publish_post');
            }
        }
    }

    /**
     * 文章保存/更新 → 仅在 update 触发开启时调度流水线
     *
     * V16修复：
     *  1. update 触发与 publish 隐式联动 — 若用户开启了 publish 但未显式关闭 update，
     *     则 update 自动生效（更新已发布文章理应触发重处理）。
     *  2. 自动化规则与流水线并行执行 — 不再互斥（与 handlePublish 行为一致）。
     */
    public static function handleSave(int $postId): void
    {
        // 请求级去重
        $dedupKey = "save_{$postId}";
        if (isset(self::$enqueued[$dedupKey])) return;
        self::$enqueued[$dedupKey] = true;

        if (wp_is_post_autosave($postId) || wp_is_post_revision($postId)) {
            return;
        }

        $post = get_post($postId);
        if (!$post || $post->post_type !== 'post') {
            return;
        }

        // 跳过新文章（由 publish_post 处理）
        // 注意：发布瞬间 post_date === post_modified，保存草稿不会经过此路径
        if ($post->post_status === 'publish' && $post->post_date === $post->post_modified) {
            return;
        }

        if ($post->post_status !== 'publish') {
            return;
        }

        // ── 检查流水线 update 触发是否启用 ──
        $triggers = Config::instance()->get('pipeline.triggers', []);
        $defaultOn = empty($triggers);
        // V16：publish 开启时 update 隐式联动 — 更新已发布文章应当重新处理
        $updateOn = $defaultOn || in_array('update', $triggers, true)
            || in_array('publish', $triggers, true);

        // ── 规则和流水线并行（与 handlePublish 行为一致）──
        $hasRules = class_exists(RuleEngine::class)
            && RuleEngine::instance()->hasEnabledRules('save_post');

        if ($hasRules) {
            PendingQueue::addRuleEvaluation('save_post', [
                'post_id'     => $postId,
                'post_title'  => $post->post_title,
                'post_type'   => $post->post_type,
                'post_author' => $post->post_author,
            ]);
        }

        if ($updateOn) {
            QueueManager::enqueue($postId, [], 'update');
        }
    }

    // ═══════════════════════════════════════════════
    // 导入事件
    // ═══════════════════════════════════════════════

    /**
     * 导入单篇文章 → 触发流水线
     */
    public static function handleImportPost(int $postId): void
    {
        if (wp_is_post_autosave($postId) || wp_is_post_revision($postId)) {
            return;
        }
        $post = get_post($postId);
        if (!$post || $post->post_type !== 'post' || $post->post_status !== 'publish') {
            return;
        }
        Logger::info('event_trigger', "import_post 触发 → 文章 {$postId}");
        self::handlePublish($postId);
    }

    /**
     * 导入完成 → 触发全量扫描兜底
     */
    public static function handleImportEnd(): void
    {
        Logger::info('event_trigger', '导入完成，触发全量扫描');
        // 直接执行扫描，不依赖定时调度
        self::scheduleScan();
    }

    // ═══════════════════════════════════════════════
    // 定时扫描
    // ═══════════════════════════════════════════════

    /**
     * 定时扫描未处理的文章 — 游标式全站扫描
     *
     * 首次开启时扫描全站所有未处理的 publish 文章（不限日期），
     * 后续每次 cron 触发时从上次游标继续，直至全部处理完毕。
     * 新发布的文章通过优先注入机制跳过排队，立即处理。
     */
    /**
     * 定时扫描 — 每批取 batchLimit 篇，新文章优先，按日期游标推进
     *
     * 游标 = 上批最后一篇文章的日期。用 date_query(before, inclusive) 继续取更老文章。
     * 同时 post__not_in 排除当批ID，防止同日期文章被 date_query 重复捞出。
     */
    public static function scheduleScan(): void
    {
        $triggers = Config::instance()->get('pipeline.triggers', []);
        if (!in_array('schedule', $triggers, true)) {
            return;
        }

        // V9：水位控制 — 总积压超过上限时暂停历史扫描，降到下限以下才恢复
        if (!QueueManager::shouldResumeScan()) {
            $backlog = QueueManager::getTotalBacklog();
            // 只在刚触及上限时记一次日志（30分钟节流）
            $logKey = 'xzd_scan_paused_log';
            if (!get_transient($logKey)) {
                set_transient($logKey, time(), 1800);
                Logger::info('event_trigger', "队列水位过高({$backlog}/" . QueueManager::MAX_BACKLOG . ")，暂停历史扫描，等待降到" . QueueManager::RESUME_AT);
            }
            // ★ V3.1.7+ 修复：水位暂停时清除 skip_ids，防止积压期间旧 ID 残留
            //   导致恢复后游标卡死（skip_ids 指向的旧文章已是 all_completed，但始终被跳过）
            $state = get_option('xzd_scan_state', null);
            if (!empty($state['skip_ids'])) {
                $state['skip_ids'] = [];
                $state['updated_at'] = current_time('mysql');
                update_option('xzd_scan_state', $state);
            }
            return;
        }

        $batchLimit = Config::instance()->int('pipeline.scan_batch_limit', 10);
        $state = get_option('xzd_scan_state', null);
        $round = (int)($state['round'] ?? 1);
        $cursorDate = $state['cursor_date'] ?? '';
        $skipIds = $state['skip_ids'] ?? [];

        // V8：分轮扫描窗口，先近后远 — 优先处理最新未处理文章
        $scanWindows = [
            1 => date('Y-m-d H:i:s', time() - 7 * DAY_IN_SECONDS),   // 最近7天
            2 => date('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS),  // 最近30天
            3 => '',   // 全部历史
        ];

        // 首轮：从7天前开始
        if (!$state) {
            $cursorDate = $scanWindows[1];
        }

        $queryArgs = [
            'post_type'      => 'post',
            'post_status'    => 'publish',
            'posts_per_page' => $batchLimit,
            'orderby'        => 'date',
            'order'          => 'DESC',
            'fields'         => 'ids',
            'no_found_rows'  => true,
        ];
        if ($cursorDate) {
            $queryArgs['date_query'] = [['before' => $cursorDate, 'inclusive' => true]];
        }
        if (!empty($skipIds)) {
            $queryArgs['post__not_in'] = array_map('intval', $skipIds);
        }
        $postIds = get_posts($queryArgs);

        // 本轮窗口扫描完毕，进入下一轮（扩大时间窗口）
        if (empty($postIds)) {
            if ($round < 3) {
                update_option('xzd_scan_state', [
                    'round'       => $round + 1,
                    'cursor_date' => $scanWindows[$round + 1],
                    'skip_ids'    => [],
                    'updated_at'  => current_time('mysql'),
                ]);
                Logger::info('event_trigger', "第{$round}轮扫描完成，进入第" . ($round + 1) . "轮（扩大窗口）");
            } else {
                delete_option('xzd_scan_state');
                Logger::info('event_trigger', "全部3轮历史扫描完成");
            }
            return;
        }

        // 逐篇 isFullyProcessed 过滤
        $toEnqueue = [];
        foreach ($postIds as $pid) {
            if (!PipelineEngine::isFullyProcessed((int)$pid, 'publish_post')) {
                $toEnqueue[] = (int)$pid;
            }
        }

        $enqueued = 0;
        if (!empty($toEnqueue)) {
            $enqueued = QueueManager::enqueueBatch($toEnqueue, [], 'schedule_scan');
        }

        $lastPost = get_post(end($postIds));
        $newCursor = $lastPost ? $lastPost->post_date : $cursorDate;

        $stateData = [
            'round'       => $round,
            'cursor_date' => $newCursor,
            'skip_ids'    => $postIds,
            'updated_at'  => current_time('mysql'),
        ];
        if (get_option('xzd_scan_state') === false) {
            add_option('xzd_scan_state', $stateData, '', 'no');
        } else {
            wp_cache_delete('xzd_scan_state', 'options');
            update_option('xzd_scan_state', $stateData, false);
        }

        Logger::info('event_trigger', "第{$round}轮扫描: 检查" . count($postIds) . "篇, 入队{$enqueued}篇, 游标:{$newCursor}");
    }

    // ═══════════════════════════════════════════════
    // 用户事件 → 自动化规则
    // ═══════════════════════════════════════════════

    /**
     * 用户注册
     */
    public static function handleUserRegister(int $userId): void
    {
        if (!class_exists(RuleEngine::class)) {
            return;
        }

        // 仅当有启用的规则时才入队
        if (!RuleEngine::instance()->hasEnabledRules('user_register')) {
            return;
        }

        $user = get_userdata($userId);

        PendingQueue::addRuleEvaluation('user_register', [
            'user_id'       => $userId,
            'user_login'    => $user ? $user->user_login : '',
            'user_email'    => $user ? $user->user_email : '',
            'user_roles'    => $user ? $user->roles : [],
            'registered_at' => $user ? $user->user_registered : '',
        ]);
    }

    /**
     * 用户登录
     */
    public static function handleLogin(string $login, \WP_User $user): void
    {
        if (!class_exists(RuleEngine::class)) {
            return;
        }

        if (!RuleEngine::instance()->hasEnabledRules('wp_login')) {
            return;
        }

        PendingQueue::addRuleEvaluation('wp_login', [
            'user_id'    => $user->ID,
            'user_login' => $login,
            'user_roles' => $user->roles,
        ]);
    }

    /**
     * 新评论 → AI审核 → 异步调度规则评估（防止AI回复阻塞用户评论）
     *
     * 流程：
     *   comment_post → wp_moderate_comment →
     *     approve → PendingQueue → RuleEngine → wp_reply_comment
     *     pending → 记录日志（等待人工）
     *     spam   → wp_spam_comment()
     *     trash  → wp_trash_comment()
     */
    public static function handleComment(int $commentId, int|string $approved): void
    {
        if (!class_exists(RuleEngine::class)) {
            return;
        }

        $comment = get_comment($commentId);
        if (!$comment) {
            return;
        }

        // 跳过AI自身回复，防止递归死循环
        if (!empty($comment->comment_parent)) {
            return;
        }

        // 阶段1：AI评论审核（先审后发 — 对所有评论执行，包括pending状态）
        try {
            $moderationResult = CommentModerator::moderate([
                'comment_id'    => $commentId,
                'auto_moderate' => true,
                'strictness'    => \XZD\AI\V5\Core\Config::instance()->get('tool_defaults.wp_moderate_comment.strictness', 'medium'),
            ], 0);
        } catch (\Throwable $e) {
            // 审核异常不阻塞评论流程
            \XZD\AI\V5\Core\Logger::warning('event_trigger', "评论审核异常: " . $e->getMessage(), ['comment_id' => $commentId]);
        }

        // 阶段2：仅已审批通过的评论才触发自动回复规则
        $comment = get_comment($commentId); // 重新获取（审核可能已改变状态）
        if (!$comment || $comment->comment_approved !== '1') {
            return;
        }

        self::scheduleCommentReply($commentId, $comment);
    }

    /**
     * 评论审核通过（transition_comment_status 钩子）
     *
     * 当评论从非已审状态转为已审时，仅调度自动回复，不重复审核。
     * 这与 handleComment 不同：handleComment 负责"审核+回复"全流程，
     * 而此处只处理"人工审核通过后触发自动回复"的场景。
     */
    public static function handleCommentApproved(string $newStatus, string $oldStatus, $comment): void
    {
        if ($newStatus !== 'approved' || $oldStatus === 'approved') {
            return;
        }

        $commentId = $comment->comment_ID;
        $commentObj = get_comment($commentId);

        // 跳过AI自身回复
        if (!$commentObj || !empty($commentObj->comment_parent)) {
            return;
        }

        // 跳过已被 handleComment 处理过的（审核元数据存在 = 本轮已走完整流程）
        $meta = get_comment_meta($commentId, '_xzd_ai_moderation', true);
        if (!empty($meta) && ($meta['moderated_by'] ?? '') === 'ai') {
            return;
        }

        self::scheduleCommentReply($commentId, $commentObj);
    }

    /**
     * 调度评论自动回复（入 PendingQueue）
     */
    private static function scheduleCommentReply(int $commentId, \WP_Comment $comment): void
    {
        if (!class_exists(RuleEngine::class)) {
            return;
        }

        if (!RuleEngine::instance()->hasEnabledRules('comment_post')) {
            return;
        }

        // 轻量入队：不阻塞用户评论请求（<5ms）
        PendingQueue::addRuleEvaluation('comment_post', [
            'comment_id'       => $commentId,
            'comment_post_id'  => (int)$comment->comment_post_ID,
            'comment_author'   => $comment->comment_author,
            'comment_content'  => $comment->comment_content,
            'comment_approved' => $comment->comment_approved,
            'user_id'          => (int)$comment->user_id,
        ]);
    }

    /**
     * 友链申请 → 异步调度规则评估
     */
    public static function handleLinkSubmitted(int $linkId): void
    {
        if (!class_exists(RuleEngine::class)) {
            return;
        }

        if (!RuleEngine::instance()->hasEnabledRules('link_submitted')) {
            return;
        }

        $link = get_bookmark($linkId);
        if (!$link) {
            return;
        }

        PendingQueue::addRuleEvaluation('link_submitted', [
            'link_id'    => $linkId,
            'link_url'   => $link->link_url,
            'link_name'  => $link->link_name,
            'link_target'=> $link->link_target,
            'link_owner' => $link->link_owner,
        ]);
    }

    /**
     * 获取钩子注册状态
     */
    public static function getStatus(): array
    {
        return [
            'initialized' => self::$initialized,
            'hooks'       => [
                'publish_post'         => has_action('publish_post', [self::class, 'handlePublish']),
                'save_post'            => has_action('save_post', [self::class, 'handleSave']),
                'xzd_ai_schedule_scan' => has_action('xzd_ai_schedule_scan', [self::class, 'scheduleScan']),
                'user_register'        => has_action('user_register', [self::class, 'handleUserRegister']),
                'wp_login'             => has_action('wp_login', [self::class, 'handleLogin']),
                'comment_post'         => has_action('comment_post', [self::class, 'handleComment']),
                'async_stage'          => has_action('xzd_ai_pipeline_async_stage', [PipelineEngine::class, 'executeAsyncStage']),
            ],
        ];
    }
}
