<?php

namespace HaoZiTeam\AIPost\Service\Features;

defined('ABSPATH') || exit;

class AdminChatStreamAjax
{
    public static function register(): void
    {
        add_action('wp_ajax_aipost_admin_chat_stream', [__CLASS__, 'handle_stream']);
        add_action('wp_ajax_aipost_admin_chat_models', [__CLASS__, 'handle_models']);
        add_action('wp_ajax_aipost_admin_chat_sessions', [__CLASS__, 'handle_sessions']);
        add_action('wp_ajax_aipost_admin_chat_session_create', [__CLASS__, 'handle_session_create']);
        add_action('wp_ajax_aipost_admin_chat_session_delete', [__CLASS__, 'handle_session_delete']);
        add_action('wp_ajax_aipost_admin_chat_messages', [__CLASS__, 'handle_messages']);
        add_action('wp_ajax_aipost_admin_chat_message_send', [__CLASS__, 'handle_message_send']);
    }

    private static function permission_check(): bool
    {
        return is_user_logged_in() && current_user_can('manage_options');
    }

    public static function handle_models(): void
    {
        \check_ajax_referer('aipost_admin_chat');
        if (!self::permission_check()) {
            \wp_send_json_error(['message' => 'forbidden'], 403);
        }

        try {
            $models = EditorGenerateUI::build_model_options();
        } catch (\Throwable $e) {
            $models = [
                'provider' => 'multi',
                'default' => '',
                'options' => [],
                'allow' => [],
                'enabled_providers' => [],
            ];
        }

        \wp_send_json_success([
            'ok' => true,
            'models' => $models,
        ]);
    }

    public static function handle_sessions(): void
    {
        \check_ajax_referer('aipost_admin_chat');
        if (!self::permission_check()) {
            \wp_send_json_error(['message' => 'forbidden'], 403);
        }

        global $wpdb;
        $sessions = AdminChatTables::table_sessions();
        $messages = AdminChatTables::table_messages();
        $uid = (int) get_current_user_id();

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, title, model_key, model_name, created_at, updated_at FROM {$sessions} WHERE user_id=%d AND deleted_at IS NULL ORDER BY updated_at DESC LIMIT 50",
                $uid
            ),
            ARRAY_A
        );
        $rows = is_array($rows) ? $rows : [];

        $sessionIds = [];
        foreach ($rows as $r) {
            $sessionIds[] = (int) ($r['id'] ?? 0);
        }

        $lastBySession = [];
        if (!empty($sessionIds)) {
            $in = implode(',', array_map('intval', $sessionIds));
            $lastRows = $wpdb->get_results(
                "SELECT m.session_id, m.content FROM {$messages} m
                 INNER JOIN (
                    SELECT session_id, MAX(id) AS max_id FROM {$messages}
                    WHERE session_id IN ({$in})
                    GROUP BY session_id
                 ) t ON t.session_id=m.session_id AND t.max_id=m.id",
                ARRAY_A
            );
            if (is_array($lastRows)) {
                foreach ($lastRows as $lr) {
                    $sid = (int)($lr['session_id'] ?? 0);
                    $c = (string)($lr['content'] ?? '');
                    $c = wp_strip_all_tags($c);
                    if (function_exists('mb_substr')) {
                        $c = (string) mb_substr($c, 0, 80, 'UTF-8');
                    } else {
                        $c = substr($c, 0, 80);
                    }
                    $lastBySession[$sid] = $c;
                }
            }
        }

        $out = [];
        foreach ($rows as $r) {
            $sid = (int) ($r['id'] ?? 0);
            $out[] = [
                'id' => $sid,
                'title' => (string)($r['title'] ?? ''),
                'model_key' => (string)($r['model_key'] ?? ''),
                'model_name' => (string)($r['model_name'] ?? ''),
                'created_at' => (string)($r['created_at'] ?? ''),
                'updated_at' => (string)($r['updated_at'] ?? ''),
                'last_message_excerpt' => (string)($lastBySession[$sid] ?? ''),
            ];
        }

        \wp_send_json_success([
            'ok' => true,
            'sessions' => $out,
        ]);
    }

    public static function handle_session_create(): void
    {
        \check_ajax_referer('aipost_admin_chat');
        if (!self::permission_check()) {
            \wp_send_json_error(['message' => 'forbidden'], 403);
        }

        global $wpdb;
        $sessions = AdminChatTables::table_sessions();
        $uid = (int) get_current_user_id();

        $title = isset($_POST['title']) ? trim((string)$_POST['title']) : '';
        $modelVal = isset($_POST['model']) ? trim((string)$_POST['model']) : '';
        $pm = self::parse_model_value($modelVal);
        $model_key = (string)($pm['provider'] ?? '');
        $model_name = (string)($pm['model'] ?? '');

        $now = current_time('mysql');
        $wpdb->insert($sessions, [
            'user_id' => $uid,
            'title' => $title,
            'model_key' => $model_key,
            'model_name' => $model_name,
            'created_at' => $now,
            'updated_at' => $now,
            'deleted_at' => null,
        ]);

        $id = (int) $wpdb->insert_id;
        \wp_send_json_success([
            'ok' => true,
            'session' => [
                'id' => $id,
                'title' => $title,
                'model_key' => $model_key,
                'model_name' => $model_name,
                'created_at' => $now,
                'updated_at' => $now,
            ],
        ]);
    }

    public static function handle_session_delete(): void
    {
        \check_ajax_referer('aipost_admin_chat');
        if (!self::permission_check()) {
            \wp_send_json_error(['message' => 'forbidden'], 403);
        }

        $session_id = isset($_POST['session_id']) ? (int)$_POST['session_id'] : 0;
        if ($session_id <= 0) {
            \wp_send_json_error(['message' => 'invalid_session_id'], 400);
        }

        $row = self::get_session_row($session_id);
        if (!$row) {
            \wp_send_json_error(['message' => 'not_found'], 404);
        }

        global $wpdb;
        $sessions = AdminChatTables::table_sessions();
        $messages = AdminChatTables::table_messages();
        $wpdb->delete($messages, [ 'session_id' => $session_id ]);
        $wpdb->delete($sessions, [ 'id' => $session_id ]);
        \wp_send_json_success([ 'ok' => true ]);
    }

    public static function handle_messages(): void
    {
        \check_ajax_referer('aipost_admin_chat');
        if (!self::permission_check()) {
            \wp_send_json_error(['message' => 'forbidden'], 403);
        }

        $session_id = isset($_POST['session_id']) ? (int)$_POST['session_id'] : 0;
        if ($session_id <= 0) {
            \wp_send_json_error(['message' => 'invalid_session_id'], 400);
        }

        $row = self::get_session_row($session_id);
        if (!$row) {
            \wp_send_json_error(['message' => 'not_found'], 404);
        }

        global $wpdb;
        $messages = AdminChatTables::table_messages();
        $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 50;
        if ($limit < 1) { $limit = 50; }
        if ($limit > 200) { $limit = 200; }

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, role, content, tokens_prompt, tokens_completion, created_at FROM {$messages} WHERE session_id=%d ORDER BY id DESC LIMIT %d",
                $session_id,
                $limit
            ),
            ARRAY_A
        );
        $rows = is_array($rows) ? array_reverse($rows) : [];

        \wp_send_json_success([
            'ok' => true,
            'session' => [
                'id' => $session_id,
                'title' => (string)($row['title'] ?? ''),
                'model_key' => (string)($row['model_key'] ?? ''),
                'model_name' => (string)($row['model_name'] ?? ''),
            ],
            'messages' => $rows,
        ]);
    }

    public static function handle_message_send(): void
    {
        \check_ajax_referer('aipost_admin_chat');
        if (!self::permission_check()) {
            \wp_send_json_error(['message' => 'forbidden'], 403);
        }

        $session_id = isset($_POST['session_id']) ? (int)$_POST['session_id'] : 0;
        if ($session_id <= 0) {
            \wp_send_json_error(['message' => 'invalid_session_id'], 400);
        }
        $row = self::get_session_row($session_id);
        if (!$row) {
            \wp_send_json_error(['message' => 'not_found'], 404);
        }

        $content = isset($_POST['content']) ? trim((string)$_POST['content']) : '';
        if ($content === '') {
            \wp_send_json_error(['message' => 'content_required'], 400);
        }

        $modelVal = isset($_POST['model']) ? trim((string)$_POST['model']) : '';
        $pm = self::parse_model_value($modelVal);
        $provider = (string)($pm['provider'] ?? '');
        $model = (string)($pm['model'] ?? '');

        global $wpdb;
        $sessions = AdminChatTables::table_sessions();
        $messages = AdminChatTables::table_messages();

        $now = current_time('mysql');

        // 插入 user 消息
        $wpdb->insert($messages, [
            'session_id' => $session_id,
            'role' => 'user',
            'content' => $content,
            'tokens_prompt' => null,
            'tokens_completion' => null,
            'created_at' => $now,
        ]);
        $user_msg_id = (int) $wpdb->insert_id;

        // 更新 session 的模型选择（若本次请求携带 model）
        $new_model_key = $provider !== '' ? $provider : (string)($row['model_key'] ?? '');
        $new_model_name = $model !== '' ? $model : (string)($row['model_name'] ?? '');

        $wpdb->update($sessions, [
            'model_key' => $new_model_key,
            'model_name' => $new_model_name,
            'updated_at' => $now,
        ], [ 'id' => $session_id ]);

        $settings = get_option('ai-post', []);
        if (!is_array($settings)) {
            $settings = [];
        }

        $task_config = [
            'disable-language-guard' => true,
            'post-tags-language' => '',
            'custom-language-description' => '',
        ];
        if ($new_model_key !== '') {
            $task_config['task-api-server-type'] = $new_model_key;
        }

        $client = ApiClientService::create_api_client($settings, 'chat', $task_config, $new_model_name);
        if (!is_object($client)) {
            \wp_send_json_error(['message' => 'client_create_failed'], 500);
        }

        if (method_exists($client, 'clearMessages')) {
            $client->clearMessages();
        }

        // 拉取历史消息（最近 20 条）并灌入上下文
        $history = $wpdb->get_results($wpdb->prepare(
            "SELECT role, content FROM {$messages} WHERE session_id=%d ORDER BY id DESC LIMIT 20",
            $session_id
        ), ARRAY_A);
        $history = is_array($history) ? array_reverse($history) : [];

        foreach ($history as $m) {
            $role = strtolower(trim((string)($m['role'] ?? 'user')));
            $c = (string)($m['content'] ?? '');
            if ($c === '') {
                continue;
            }
            if ($role !== 'assistant' && $role !== 'system') {
                $role = 'user';
            }
            if (method_exists($client, 'addMessage')) {
                $client->addMessage($c, $role);
            }
        }

        $resp = $client->ask($content);
        $answer = is_array($resp) ? (string)($resp['answer'] ?? '') : '';
        $usage = is_array($resp) ? (array)($resp['usage'] ?? []) : [];

        $tokens_prompt = isset($usage['prompt_tokens']) ? (int)$usage['prompt_tokens'] : null;
        $tokens_completion = isset($usage['completion_tokens']) ? (int)$usage['completion_tokens'] : null;

        $now2 = current_time('mysql');
        $wpdb->insert($messages, [
            'session_id' => $session_id,
            'role' => 'assistant',
            'content' => $answer,
            'tokens_prompt' => $tokens_prompt,
            'tokens_completion' => $tokens_completion,
            'created_at' => $now2,
        ]);
        $assistant_msg_id = (int) $wpdb->insert_id;

        $wpdb->update($sessions, [
            'updated_at' => $now2,
        ], [ 'id' => $session_id ]);

        \wp_send_json_success([
            'ok' => true,
            'user_message' => [
                'id' => $user_msg_id,
                'role' => 'user',
                'content' => $content,
                'created_at' => $now,
            ],
            'assistant_message' => [
                'id' => $assistant_msg_id,
                'role' => 'assistant',
                'content' => $answer,
                'created_at' => $now2,
            ],
            'usage' => $usage,
        ]);
    }

    private static function parse_model_value(string $val): array
    {
        $val = trim((string)$val);
        $provider = '';
        $model = '';

        if ($val === '') {
            return [ 'provider' => '', 'model' => '' ];
        }

        if (strpos($val, '::') !== false) {
            $parts = explode('::', $val, 2);
            $provider = trim((string)($parts[0] ?? ''));
            $model = trim((string)($parts[1] ?? ''));
        } else {
            $model = $val;
        }

        return [
            'provider' => $provider,
            'model' => $model,
        ];
    }

    private static function get_session_row(int $session_id): ?array
    {
        global $wpdb;

        $sessions = AdminChatTables::table_sessions();
        $uid = (int) get_current_user_id();

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$sessions} WHERE id=%d AND user_id=%d AND deleted_at IS NULL",
                $session_id,
                $uid
            ),
            ARRAY_A
        );

        return is_array($row) ? $row : null;
    }

    /**
     * 目前仅对 OpenAI 兼容 SSE 的 provider 提供真正的流式输出。
     * 其它 provider 直接返回错误，让前端回退到非流式 REST。
     */
    private static function resolve_openai_sse_compatible(array $settings, string $provider, string $overrideModel = ''): ?array
    {
        $provider = trim((string)$provider);
        $provider = strtolower($provider);
        if ($provider === 'openai' || $provider === '') {
            $provider = 'custom';
        }

        $apiServer = '';
        $selectedModel = '';
        $apiKey = '';

        if ($provider === 'custom') {
            $accounts = $settings['openai-accounts'] ?? [];
            $apiServer = trim((string)($settings['openai-api-server'] ?? 'https://sk.slapi.cn/'));

            $apiServer = rtrim($apiServer, "/\t\n\r ");
            $apiServer = preg_replace('#/v\d+/chat/completions/?$#i', '', $apiServer);
            $apiServer = preg_replace('#/chat/completions/?$#i', '', $apiServer);
            $apiServer = rtrim((string)$apiServer, "/\t\n\r ");
            if ($apiServer === '') {
                $apiServer = 'https://sk.slapi.cn/';
            }
            if (substr($apiServer, -1) !== '/') {
                $apiServer .= '/';
            }

            $parsed = @parse_url($apiServer);
            $host = is_array($parsed) ? (string)($parsed['host'] ?? '') : '';
            $path = is_array($parsed) ? (string)($parsed['path'] ?? '') : '';
            if ($host === 'sk.slapi.cn' && ($path === '' || $path === '/')) {
                $apiServer = rtrim($apiServer, '/') . '/v1/';
            }

            $selectedModel = 'gpt-4o-mini';
            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                if ($model_selection === 'custom') {
                    $selectedModel = !empty($custom_model_name) ? (string)$custom_model_name : $selectedModel;
                } else {
                    $selectedModel = (string)$model_selection;
                }
                $apiKey = trim((string)($account['account-secret-key'] ?? ''));
            }

            if ($apiKey === '') {
                $secKey = SecureSettings::get('openai_key');
                if (!$secKey) {
                    $secKey = SecureSettings::get('custom_key');
                }
                if (is_string($secKey) && $secKey !== '') {
                    $apiKey = trim($secKey);
                }
            }
        } elseif ($provider === 'deepseek') {
            $accounts = $settings['deepseek-accounts'] ?? [];
            $apiServer = trim((string)($settings['deepseek-api-server'] ?? 'https://api.deepseek.com/v1/'));
            if ($apiServer === '') {
                $apiServer = 'https://api.deepseek.com/v1/';
            }
            $apiServer = rtrim($apiServer, "/\t\n\r ");
            $apiServer = preg_replace('#/v\d+/chat/completions/?$#i', '', $apiServer);
            $apiServer = preg_replace('#/chat/completions/?$#i', '', $apiServer);
            $apiServer = rtrim((string)$apiServer, "/\t\n\r ");
            if (substr($apiServer, -1) !== '/') {
                $apiServer .= '/';
            }

            $selectedModel = 'deepseek-chat';
            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                $selectedModel = ($model_selection === 'custom') ? ($custom_model_name ?: $selectedModel) : (string)$model_selection;
                $apiKey = trim((string)($account['account-secret-key'] ?? ''));
            }
            if ($apiKey === '') {
                $secKey = SecureSettings::get('deepseek_key');
                if (is_string($secKey) && $secKey !== '') {
                    $apiKey = trim($secKey);
                }
            }
        } elseif ($provider === 'moonshot') {
            $accounts = $settings['moonshot-accounts'] ?? [];
            $apiServer = 'https://api.moonshot.cn/v1/';
            $selectedModel = 'moonshot-v1-8k';
            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                $selectedModel = ($model_selection === 'custom') ? ($custom_model_name ?: $selectedModel) : (string)$model_selection;
                $apiKey = trim((string)($account['account-secret-key'] ?? ''));
            }
            if ($apiKey === '') {
                $secKey = SecureSettings::get('moonshot_key');
                if (is_string($secKey) && $secKey !== '') {
                    $apiKey = trim($secKey);
                }
            }
        } elseif ($provider === 'doubao') {
            $accounts = $settings['doubao-accounts'] ?? [];
            $apiServer = 'https://ark.cn-beijing.volces.com/api/v3/';
            $selectedModel = 'doubao-seed-2-0-mini-260215';
            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                $selectedModel = ($model_selection === 'custom') ? ($custom_model_name ?: $selectedModel) : (string)$model_selection;
                $apiKey = trim((string)($account['account-secret-key'] ?? ''));
            }
            if ($apiKey === '') {
                $secKey = SecureSettings::get('doubao_key');
                if (is_string($secKey) && $secKey !== '') {
                    $apiKey = trim($secKey);
                }
            }
        } elseif ($provider === 'bai-lian' || $provider === 'bailian') {
            $accounts = $settings['bai-lian-accounts'] ?? [];
            $apiServer = 'https://dashscope.aliyuncs.com/compatible-mode/v1/';
            $selectedModel = 'qwen-plus';
            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                $selectedModel = ($model_selection === 'custom') ? ($custom_model_name ?: $selectedModel) : (string)$model_selection;
                $apiKey = trim((string)($account['account-secret-key'] ?? ''));
            }
            if ($apiKey === '') {
                $secKey = SecureSettings::get('bailian_key');
                if (is_string($secKey) && $secKey !== '') {
                    $apiKey = trim($secKey);
                }
            }
        } elseif ($provider === 'zhipu') {
            $accounts = $settings['zhipu-accounts'] ?? [];
            $apiServer = 'https://open.bigmodel.cn/api/paas/v4/';
            $selectedModel = 'glm-4.7-flashx';
            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                $selectedModel = ($model_selection === 'custom') ? ($custom_model_name ?: $selectedModel) : (string)$model_selection;
                $apiKey = trim((string)($account['account-secret-key'] ?? ''));
            }
            if ($apiKey === '') {
                $secKey = SecureSettings::get('zhipu_key');
                if (is_string($secKey) && $secKey !== '') {
                    $apiKey = trim($secKey);
                }
            }
        } elseif ($provider === 'spark') {
            $accounts = $settings['spark-accounts'] ?? [];
            $apiServerV2 = 'https://spark-api-open.xf-yun.com/v2/';
            $apiServerV1 = 'https://spark-api-open.xf-yun.com/v1/';
            $apiServer = $apiServerV2;
            $selectedModel = 'spark-x';

            $apiKeyV2 = '';
            $apiKeyV1 = '';
            $apiKeyPro = '';
            $apiKeyMax = '';

            if (!empty($accounts) && is_array($accounts)) {
                $account = $accounts[rand(0, count($accounts) - 1)];
                $model_selection = $account['model-selection'] ?? $selectedModel;
                $custom_model_name = $account['custom-model-name'] ?? '';
                $selectedModel = ($model_selection === 'custom') ? ($custom_model_name ?: $selectedModel) : (string)$model_selection;

                $apiKeyV2 = trim((string)($account['account-secret-key-v2'] ?? ''));
                $apiKeyV1 = trim((string)($account['account-secret-key-v1'] ?? ''));
                $apiKeyPro = trim((string)($account['account-secret-key-pro'] ?? ''));
                $apiKeyMax = trim((string)($account['account-secret-key-max'] ?? ''));
                if ($apiKeyV2 === '') {
                    $apiKeyV2 = trim((string)($account['account-secret-key'] ?? ''));
                }
            }

            // 模型归一化（与 ApiClientService 保持一致的主要兼容别名）
            $m = trim((string)$selectedModel);
            if ($m === 'spark-x2') {
                $m = 'spark-x';
            }
            if ($m === 'x1.5' || $m === 'spark-x1.5' || $m === 'spark-x1') {
                $m = 'x1';
            }
            if ($m === '') {
                $m = 'spark-x';
            }
            $selectedModel = $m;

            if ($selectedModel === 'lite') {
                $apiServer = $apiServerV1;
                $apiKey = $apiKeyV1;
                if ($apiKey === '') {
                    $secKey = SecureSettings::get('spark_v1_key');
                    if (is_string($secKey) && trim($secKey) !== '') {
                        $apiKey = trim($secKey);
                    }
                }
            } else {
                $apiServer = $apiServerV2;
                if ($selectedModel === 'spark-pro') {
                    $apiKey = $apiKeyPro !== '' ? $apiKeyPro : $apiKeyV2;
                } elseif ($selectedModel === 'spark-max') {
                    $apiKey = $apiKeyMax !== '' ? $apiKeyMax : $apiKeyV2;
                } else {
                    $apiKey = $apiKeyV2;
                }

                if ($apiKey === '') {
                    $secKey = SecureSettings::get('spark_v2_key');
                    if (is_string($secKey) && trim($secKey) !== '') {
                        $apiKey = trim($secKey);
                    }
                }
                if ($apiKey === '') {
                    $secKey = SecureSettings::get('spark_key');
                    if (is_string($secKey) && trim($secKey) !== '') {
                        $apiKey = trim($secKey);
                    }
                }
                if ($apiKey === '' && $selectedModel === 'spark-pro') {
                    $secKey = SecureSettings::get('spark_pro_key');
                    if (is_string($secKey) && trim($secKey) !== '') {
                        $apiKey = trim($secKey);
                    }
                }
                if ($apiKey === '' && $selectedModel === 'spark-max') {
                    $secKey = SecureSettings::get('spark_max_key');
                    if (is_string($secKey) && trim($secKey) !== '') {
                        $apiKey = trim($secKey);
                    }
                }
            }
        } else {
            return null;
        }

        if (trim($overrideModel) !== '') {
            $selectedModel = trim((string)$overrideModel);
        }

        $apiServer = trim((string)$apiServer);
        if ($apiServer === '' || $apiKey === '' || $selectedModel === '') {
            return null;
        }
        if (substr($apiServer, -1) !== '/') {
            $apiServer .= '/';
        }

        $pluginRoot = dirname(__DIR__, 2);
        $forcedCABundle = $pluginRoot . '/resources/cacert.pem';
        $caBundle = @file_exists($forcedCABundle) ? $forcedCABundle : '';

        return [
            'base_url' => rtrim($apiServer, '/') . '/',
            'api_key' => $apiKey,
            'model' => $selectedModel,
            'ca_bundle' => $caBundle,
        ];
    }

    private static function sse_send(array $data): void
    {
        echo 'data: ' . wp_json_encode($data) . "\n\n";
        if (function_exists('ob_flush')) {
            @ob_flush();
        }
        if (function_exists('flush')) {
            @flush();
        }
    }

    public static function handle_stream(): void
    {
        if (!self::permission_check()) {
            wp_die('forbidden', '', [ 'response' => 403 ]);
        }

        $nonce = isset($_REQUEST['_ajax_nonce']) ? (string)$_REQUEST['_ajax_nonce'] : '';
        if (!wp_verify_nonce($nonce, 'aipost_admin_chat')) {
            wp_die('bad_nonce', '', [ 'response' => 403 ]);
        }

        $session_id = isset($_REQUEST['session_id']) ? (int)$_REQUEST['session_id'] : 0;
        $content = isset($_REQUEST['content']) ? trim((string)$_REQUEST['content']) : '';
        $modelVal = isset($_REQUEST['model']) ? trim((string)$_REQUEST['model']) : '';

        if ($session_id <= 0) {
            wp_die('bad_session', '', [ 'response' => 400 ]);
        }
        if ($content === '') {
            wp_die('content_required', '', [ 'response' => 400 ]);
        }

        $row = self::get_session_row($session_id);
        if (!$row) {
            wp_die('not_found', '', [ 'response' => 404 ]);
        }

        $pm = self::parse_model_value($modelVal);
        $provider = (string)($pm['provider'] ?? '');
        $model = (string)($pm['model'] ?? '');

        $new_model_key = $provider !== '' ? $provider : (string)($row['model_key'] ?? '');
        $new_model_name = $model !== '' ? $model : (string)($row['model_name'] ?? '');

        $settings = get_option('ai-post', []);
        if (!is_array($settings)) {
            $settings = [];
        }

        $up = self::resolve_openai_sse_compatible($settings, $new_model_key, $new_model_name);
        if (!is_array($up)) {
            wp_die('stream_not_supported', '', [ 'response' => 400 ]);
        }

        if (!function_exists('curl_init')) {
            wp_die('curl_missing', '', [ 'response' => 500 ]);
        }

        global $wpdb;
        $sessions = AdminChatTables::table_sessions();
        $messages = AdminChatTables::table_messages();

        $now = current_time('mysql');

        $wpdb->insert($messages, [
            'session_id' => $session_id,
            'role' => 'user',
            'content' => $content,
            'tokens_prompt' => null,
            'tokens_completion' => null,
            'created_at' => $now,
        ]);
        $user_msg_id = (int) $wpdb->insert_id;

        $wpdb->update($sessions, [
            'model_key' => $new_model_key,
            'model_name' => $new_model_name,
            'updated_at' => $now,
        ], [ 'id' => $session_id ]);

        while (ob_get_level() > 0) {
            @ob_end_flush();
        }
        @ini_set('output_buffering', 'off');
        @ini_set('zlib.output_compression', '0');
        @ini_set('implicit_flush', '1');
        if (function_exists('apache_setenv')) {
            @apache_setenv('no-gzip', '1');
        }
        if (function_exists('ignore_user_abort')) {
            @ignore_user_abort(true);
        }

        header('Content-Type: text/event-stream; charset=utf-8');
        header('Cache-Control: no-cache, no-transform');
        header('X-Accel-Buffering: no');
        header('Content-Encoding: none');
        header('Connection: keep-alive');

        // padding：触发部分环境（PHP-FPM/Nginx/浏览器）立刻开始渲染与传输
        echo ':' . str_repeat(' ', 2048) . "\n\n";
        if (function_exists('ob_flush')) {
            @ob_flush();
        }
        if (function_exists('flush')) {
            @flush();
        }

        self::sse_send([
            'ok' => true,
            'type' => 'meta',
            'user_message_id' => $user_msg_id,
            'session_id' => $session_id,
        ]);

        $history = $wpdb->get_results($wpdb->prepare(
            "SELECT role, content FROM {$messages} WHERE session_id=%d ORDER BY id DESC LIMIT 20",
            $session_id
        ), ARRAY_A);
        $history = is_array($history) ? array_reverse($history) : [];

        $payloadMsgs = [];
        foreach ($history as $m) {
            $role = strtolower(trim((string)($m['role'] ?? 'user')));
            $c = (string)($m['content'] ?? '');
            if ($c === '') {
                continue;
            }
            if ($role !== 'assistant' && $role !== 'system') {
                $role = 'user';
            }
            $payloadMsgs[] = [
                'role' => $role,
                'content' => $c,
            ];
        }
        if (empty($payloadMsgs)) {
            $payloadMsgs = [ [ 'role' => 'user', 'content' => $content ] ];
        }

        $endpoint = rtrim((string)$up['base_url'], '/') . '/chat/completions';
        $data = [
            'model' => (string)$up['model'],
            'messages' => $payloadMsgs,
            'stream' => true,
        ];

        $headers = [
            'Content-Type: application/json',
            'Authorization: Bearer ' . (string)$up['api_key'],
            'Accept: text/event-stream',
        ];

        $buf = '';
        $answer = '';
        $hasError = false;
        $upstreamHttpCode = 0;
        $upstreamErrorBody = '';

        $ch = curl_init($endpoint);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, wp_json_encode($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 360);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024);
        if (defined('CURLOPT_TCP_NODELAY')) {
            curl_setopt($ch, CURLOPT_TCP_NODELAY, 1);
        }
        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$upstreamHttpCode) {
            $line = (string)$header;
            if (stripos($line, 'HTTP/') === 0) {
                // HTTP/1.1 200 OK
                if (preg_match('#^HTTP/\S+\s+(\d+)#', trim($line), $m)) {
                    $upstreamHttpCode = (int)($m[1] ?? 0);
                }
            }
            return strlen($header);
        });

        $caBundle = (string)($up['ca_bundle'] ?? '');
        if ($caBundle !== '' && @file_exists($caBundle)) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_CAINFO, $caBundle);
        }

        curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) use (&$buf, &$answer, &$hasError, &$upstreamErrorBody) {
            $buf .= (string)$chunk;

            // 兼容上游使用 \r\n\r\n 分隔 SSE 事件：统一归一化到 \n
            if (strpos($buf, "\r\n") !== false) {
                $buf = str_replace("\r\n", "\n", $buf);
            }

            // upstream SSE 以 "\n\n" 分隔事件
            while (($pos = strpos($buf, "\n\n")) !== false) {
                $event = substr($buf, 0, $pos);
                $buf = substr($buf, $pos + 2);
                $event = trim((string)$event);
                if ($event === '') {
                    continue;
                }

                $lines = preg_split("/\r?\n/", $event);
                if (!is_array($lines)) {
                    continue;
                }

                foreach ($lines as $line) {
                    $line = trim((string)$line);
                    if ($line === '' || stripos($line, 'data:') !== 0) {
                        continue;
                    }

                    $json = trim(substr($line, 5));
                    if ($json === '[DONE]') {
                        AdminChatStreamAjax::sse_send([ 'ok' => true, 'type' => 'done' ]);
                        continue;
                    }

                    $obj = json_decode($json, true);
                    if (!is_array($obj)) {
                        // 若上游返回的是错误文本/HTML，留作错误体
                        $upstreamErrorBody .= $json;
                        continue;
                    }

                    // OpenAI 兼容：choices[0].delta.content
                    $delta = $obj['choices'][0]['delta']['content'] ?? '';
                    if (!is_string($delta) || $delta === '') {
                        continue;
                    }

                    $answer .= $delta;
                    AdminChatStreamAjax::sse_send([
                        'ok' => true,
                        'type' => 'delta',
                        'delta' => $delta,
                    ]);
                }
            }

            return strlen($chunk);
        });

        $ok = curl_exec($ch);
        if ($ok === false) {
            $hasError = true;
            $err = curl_error($ch);
            self::sse_send([ 'ok' => false, 'type' => 'error', 'error' => $err !== '' ? $err : 'curl_exec_failed' ]);
        }

        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode < 200 || $httpCode >= 300) {
            $hasError = true;
            // 尝试从上游错误体中提取 message
            $errMsg = 'upstream_http_' . $httpCode;
            $raw = trim((string)$upstreamErrorBody);
            if ($raw !== '') {
                $maybe = json_decode($raw, true);
                if (is_array($maybe)) {
                    $m1 = $maybe['error']['message'] ?? ($maybe['message'] ?? '');
                    if (is_string($m1) && trim($m1) !== '') {
                        $errMsg = trim($m1);
                    }
                }
            }
            self::sse_send([ 'ok' => false, 'type' => 'error', 'error' => $errMsg ]);
        }

        if (!$hasError) {
            $now2 = current_time('mysql');
            $wpdb->insert($messages, [
                'session_id' => $session_id,
                'role' => 'assistant',
                'content' => $answer,
                'tokens_prompt' => null,
                'tokens_completion' => null,
                'created_at' => $now2,
            ]);
            $assistant_msg_id = (int) $wpdb->insert_id;

            $wpdb->update($sessions, [
                'updated_at' => $now2,
            ], [ 'id' => $session_id ]);

            self::sse_send([
                'ok' => true,
                'type' => 'final',
                'assistant_message_id' => $assistant_msg_id,
            ]);
        }

        self::sse_send([ 'ok' => true, 'type' => 'end' ]);
        wp_die();
    }
}
