<?php

namespace HaoZiTeam\AIPost;

defined( 'ABSPATH' ) || exit;

use HaoZiTeam\AIPost\Service\Base;
use HaoZiTeam\AIPost\Service\Setting;

class Plugin {

    public function __construct() {
        // 运行时兜底：为全局 cURL / OpenSSL 设置 CA 证书路径（当 ini 未显式配置时）
        try {
            $cacertPath = (defined('AIPOST_PLUGIN_PATH') ? AIPOST_PLUGIN_PATH : plugin_dir_path(__FILE__)) . 'resources/cacert.pem';
            $curlInfo = function_exists('curl_version') ? curl_version() : null;
            $curlVer = $curlInfo && isset($curlInfo['version']) ? $curlInfo['version'] : 'unknown';
            $sslVer  = $curlInfo && isset($curlInfo['ssl_version']) ? $curlInfo['ssl_version'] : 'unknown';
            $iniCurl = ini_get('curl.cainfo');
            $iniOpen = ini_get('openssl.cafile');

            error_log(sprintf(
                'AI Post SSL: 启动诊断(Plugin.php): curl %s / ssl %s; curl.cainfo=%s; openssl.cafile=%s',
                $curlVer,
                $sslVer,
                empty($iniCurl) ? 'empty' : $iniCurl,
                empty($iniOpen) ? 'empty' : $iniOpen
            ));

            if (is_readable($cacertPath)) {
                $setAny = false;
                if (empty($iniCurl)) {
                    @ini_set('curl.cainfo', $cacertPath);
                    $setAny = true;
                }
                if (empty($iniOpen)) {
                    @ini_set('openssl.cafile', $cacertPath);
                    $setAny = true;
                }

                if ($setAny) {
                    $size = @filesize($cacertPath);
                    error_log(sprintf(
                        'AI Post SSL: 已在运行时设置全局 CA 证书路径为: %s (readable=%s, size=%s bytes)',
                        $cacertPath,
                        is_readable($cacertPath) ? 'yes' : 'no',
                        $size !== false ? (string)$size : 'unknown'
                    ));
                }
            } else {
                error_log(sprintf('AI Post SSL: 警告: 预期的证书文件不可读: %s', $cacertPath));
            }
        } catch (\Throwable $e) {
            error_log('AI Post SSL: 运行时兜底设置异常: ' . $e->getMessage());
        }

        // 升级/正常加载路径：确保字体挂载目录存在（避免必须停用-启用）
        try {
            $this->ensure_font_mount_dir();
        } catch (\Throwable $e) {
            // 静默失败不影响主流程
        }

        // 根据需求移除设置页的“标题图字体挂载（方式二）”提示面板，保留后台整洁度。

        // 升级后的一次性默认字体下载（若已配置 URL 且本地缺失则执行一次）
        try {
            $this->maybe_download_default_font_once();
        } catch (\Throwable $e) {
            // 静默，不影响主流程
        }

        // 运行期兜底：每次请求早期检查一次（有节流），若字体缺失则尝试按候选URL自动下载
        // 注意：与 maybe_download_default_font_once 不冲突；此处不受版本标记限制，仅受节流与是否缺失控制
        try {
            \add_action('init', [$this, 'ensure_default_font_present_runtime']);
            if (is_admin()) {
                \add_action('admin_init', [$this, 'ensure_default_font_present_runtime']);
            }
        } catch (\Throwable $e) {
            // 静默
        }

        // 注册字体上传 AJAX 接口（前端注入已迁移至 Service\Setting::render_font_tools_inline）
        if (is_admin()) {
            \add_action('wp_ajax_aipost_upload_font', [$this, 'ajax_upload_font']);
            \add_action('wp_ajax_aipost_check_font_status', [$this, 'ajax_check_font_status']);
            \add_action('wp_ajax_aipost_download_default_font_now', [$this, 'ajax_download_default_font_now']);
        }

        // 预置默认的字体下载候选 URL（可被主题/插件通过相同过滤器追加或覆盖）
        \add_filter('aipost_font_candidates_wei_ruan_ya_hei', function($candidates){
            if (!is_array($candidates)) { $candidates = []; }
            $defaults = [
				'https://download.2090ai.cn/WeiRuanYaHei.ttf',
                'https://video.2090ai.com/WeiRuanYaHei.ttf',
            ];
            // 合并且去重
            $all = array_values(array_unique(array_filter(array_merge($defaults, $candidates))));
            return $all;
        }, 5);

        // 自动自部署 MU 文件：ssl-cainfo.php（仅在不存在时写入，不覆盖）
        try {
            if (defined('WP_CONTENT_DIR')) {
                $muDir  = rtrim(WP_CONTENT_DIR, '/\\') . DIRECTORY_SEPARATOR . 'mu-plugins';
                $muFile = $muDir . DIRECTORY_SEPARATOR . 'ssl-cainfo.php';

                if (!file_exists($muFile)) {
                    if (!function_exists('wp_mkdir_p')) {
                        // 尝试引入以确保在早期也可用
                        if (file_exists(ABSPATH . 'wp-admin/includes/file.php')) {
                            @require_once ABSPATH . 'wp-admin/includes/file.php';
                        }
                    }

                    if (function_exists('wp_mkdir_p')) {
                        @wp_mkdir_p($muDir);
                    } else {
                        if (!is_dir($muDir)) {
                            @mkdir($muDir, 0755, true);
                        }
                    }

                    $muContent = <<<'PHP'
<?php
// Auto-deployed by ai-post: ssl-cainfo.php
// 作用：在 PHP 未显式配置 CA 证书路径时，为 curl.cainfo 与 openssl.cafile 指定可读的 cacert.pem，增强全局 SSL 稳定性。
if (!defined('ABSPATH')) { exit; }

try {
    $curlInfo = function_exists('curl_version') ? curl_version() : null;
    $curlVer  = $curlInfo && isset($curlInfo['version']) ? $curlInfo['version'] : 'unknown';
    $sslVer   = $curlInfo && isset($curlInfo['ssl_version']) ? $curlInfo['ssl_version'] : 'unknown';
    $iniCurl  = ini_get('curl.cainfo');
    $iniOpen  = ini_get('openssl.cafile');

    error_log(sprintf(
        'AI Post SSL(MU): 启动诊断: curl %s / ssl %s; curl.cainfo=%s; openssl.cafile=%s',
        $curlVer,
        $sslVer,
        empty($iniCurl) ? 'empty' : $iniCurl,
        empty($iniOpen) ? 'empty' : $iniOpen
    ));

    // 候选路径优先级：env -> 插件资源 -> uploads
    $candidates = array();
    $env1 = getenv('CURL_CA_BUNDLE');
    $env2 = getenv('SSL_CERT_FILE');
    if (!empty($env1)) $candidates[] = $env1;
    if (!empty($env2)) $candidates[] = $env2;

    if (defined('WP_CONTENT_DIR')) {
        $candidates[] = rtrim(WP_CONTENT_DIR, '/\\') . '/plugins/ai-post/resources/cacert.pem';
        $candidates[] = rtrim(WP_CONTENT_DIR, '/\\') . '/uploads/cacert.pem';
    }

    $resolved = null;
    foreach ($candidates as $p) {
        if (is_string($p) && $p !== '' && @is_readable($p)) { $resolved = $p; break; }
    }

    if ($resolved) {
        $setAny = false;
        if (empty($iniCurl)) { @ini_set('curl.cainfo', $resolved); $setAny = true; }
        if (empty($iniOpen)) { @ini_set('openssl.cafile', $resolved); $setAny = true; }

        if ($setAny) {
            $size = @filesize($resolved);
            error_log(sprintf(
                'AI Post SSL(MU): 已自动设置 CA 证书路径为: %s (readable=%s, size=%s bytes)',
                $resolved,
                is_readable($resolved) ? 'yes' : 'no',
                $size !== false ? (string)$size : 'unknown'
            ));
        }
    } else {
        error_log('AI Post SSL(MU): 未找到可用的 CA 证书路径候选');
    }
} catch (\Throwable $e) {
    error_log('AI Post SSL(MU): 运行异常: ' . $e->getMessage());
}
PHP;

                    $written = @file_put_contents($muFile, $muContent);
                    if ($written !== false) {
                        error_log(sprintf('AI Post SSL: 已自动部署 MU 文件: %s', $muFile));
                    } else {
                        error_log(sprintf('AI Post SSL: 自动部署 MU 文件失败(写入失败): %s', $muFile));
                    }
                }
            }
        } catch (\Throwable $e) {
            error_log('AI Post SSL: 自动自部署 MU 文件异常: ' . $e->getMessage());
        }

        // 运行时调试日志开关：不修改 wp-config.php，根据设置决定是否将 PHP 错误写入 debug.log
        try {
            $opts = \get_option('ai-post', []);
            $enableDebug = false;
            if (is_array($opts)) {
                // 兼容旧版（字段在顶层）
                if (!empty($opts['enable-debug-log'])) {
                    $enableDebug = true;
                }
                // 新版：字段位于 tabbed 字段组 global_tabs 中
                if (!$enableDebug && !empty($opts['global_tabs']) && is_array($opts['global_tabs'])) {
                    $enableDebug = !empty($opts['global_tabs']['enable-debug-log']);
                }
            }
            if ($enableDebug) {
                // 优先写入站点根目录 debug.log
                $target = rtrim(defined('ABSPATH') ? ABSPATH : dirname(__FILE__, 3), '/\\') . DIRECTORY_SEPARATOR . 'debug.log';

                // 若根目录不可写，回退到 uploads/ai-post/logs/debug.log
                $fallback = null;
                if (!@is_writable(dirname($target))) {
                    if (defined('WP_CONTENT_DIR')) {
                        $uploadsDir = rtrim(WP_CONTENT_DIR, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'ai-post' . DIRECTORY_SEPARATOR . 'logs';
                        if (!function_exists('wp_mkdir_p')) {
                            if (file_exists(ABSPATH . 'wp-admin/includes/file.php')) {
                                @require_once ABSPATH . 'wp-admin/includes/file.php';
                            }
                        }
                        if (function_exists('wp_mkdir_p')) { @wp_mkdir_p($uploadsDir); } else { @mkdir($uploadsDir, 0755, true); }
                        if (@is_dir($uploadsDir) && @is_writable($uploadsDir)) {
                            $fallback = $uploadsDir . DIRECTORY_SEPARATOR . 'debug.log';
                        }
                    }
                }

                $logFile = $fallback ?: $target;

                @ini_set('log_errors', '1');
                @ini_set('display_errors', '0');
                @ini_set('error_log', $logFile);

                // 记录一次性诊断
                error_log(sprintf('AI Post Debug: 运行时已启用错误日志记录到 %s (fallback=%s)', $logFile, $fallback ? 'yes' : 'no'));
                // 追加：在日志重定向后，再输出一次当前 CA 状态，但做节流（30 分钟内仅打印一次）
                try {
                    $throttle_key = 'aipost_ca_status_last_log';
                    $last = function_exists('get_transient') ? get_transient($throttle_key) : false;
                    if (!$last) {
                        $iniCurl = ini_get('curl.cainfo');
                        $iniOpen = ini_get('openssl.cafile');
                        $cacertPath = (defined('AIPOST_PLUGIN_PATH') ? AIPOST_PLUGIN_PATH : plugin_dir_path(__FILE__)) . 'resources/cacert.pem';
                        $readable  = is_readable($cacertPath) ? 'yes' : 'no';
                        $size      = @filesize($cacertPath);
                        error_log(sprintf(
                            'AI Post SSL: 当前 CA 配置: curl.cainfo=%s; openssl.cafile=%s; plugin_cacert=%s (readable=%s, size=%s bytes)',
                            empty($iniCurl) ? 'empty' : $iniCurl,
                            empty($iniOpen) ? 'empty' : $iniOpen,
                            $cacertPath,
                            $readable,
                            $size !== false ? (string)$size : 'unknown'
                        ));
                        if (function_exists('set_transient')) {
                            set_transient($throttle_key, time(), 30 * MINUTE_IN_SECONDS);
                        }
                    }
                } catch (\Throwable $e) {
                    error_log('AI Post SSL: 输出当前 CA 配置失败: ' . $e->getMessage());
                }
            }
        } catch (\Throwable $e) {
            error_log('AI Post Debug: 启用运行时调试日志失败: ' . $e->getMessage());
        }

        // 在多站点环境中正确注册菜单
        if (is_multisite()) {
            // 注册主站点菜单
            if (is_main_site()) {
                \add_action('admin_menu', array($this, 'admin_menu'));
            }
            
            // 注册子站点菜单
            \add_action('admin_menu', array($this, 'site_admin_menu'));
            
            // 移除冗余子菜单
            \add_action('admin_menu', array($this, 'remove_submenu'), 999);
        } else {
            // 单站点环境
            \add_action('admin_menu', array($this, 'admin_menu'));
            \add_action('admin_menu', array($this, 'remove_submenu'), 999);
        }
        
        // 初始化基础服务
        new Base();
        new Setting();

        // 确保在插件设置页可以加载前端资源与渲染令牌面板
        \add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'), 999);
        \add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'), 999);
        // 令牌面板已移动至“系统调试与日志”Tab 内，由 Setting::render_debug_log_panel 渲染

        // 安妮聊天：仅在插件设置页注入抽屉容器
        \add_action('admin_notices', array($this, 'render_admin_chat_entry'));
        \add_action('admin_footer', array($this, 'render_admin_chat_root'));

        // 后台设置页：注入前端安全保存令牌工具脚本
        // \add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
        // 依据用户反馈，移除底部“令牌快速保存(REST)”面板的渲染挂载，保留脚本以便后续在表单内联集成
        // \add_action('admin_footer', array($this, 'render_secure_token_panel'));

        // 多站点支持 - 确保插件在网络激活时正确处理
        if (is_multisite()) {
            // 在网络管理界面添加设置链接
            add_filter('network_admin_plugin_action_links_' . plugin_basename(PLUGIN_FILE), [$this, 'plugin_action_links']);
            
            // 在各子站点添加设置链接
            add_filter('plugin_action_links_' . plugin_basename(PLUGIN_FILE), [$this, 'plugin_action_links']);
        } else {
            // 单站点模式下添加设置链接
            add_filter('plugin_action_links_' . plugin_basename(PLUGIN_FILE), [$this, 'plugin_action_links']);
        }
    }

    /**
     * 运行期兜底：在 init/admin_init 时检查字体是否缺失，若缺失则按候选URL下载一次。
     * 通过 transient 进行节流，避免频繁远程请求（默认10分钟）。
     */
    public function ensure_default_font_present_runtime(): void
    {
        // 运行期检测：节流 + 缺失即尝试下载
        $this->attempt_default_font_download(false);
    }

    /**
     * 抽取的下载逻辑：当字体缺失时，按候选URL下载；支持强制模式绕过节流。
     * @param bool $force 是否绕过节流并立刻尝试
     * @return array 包含 exists/downloading/message
     */
    private function attempt_default_font_download(bool $force = false): array
    {
        if (!function_exists('wp_upload_dir')) { require_once ABSPATH . 'wp-includes/functions.php'; }
        if (!function_exists('wp_mkdir_p')) { require_once ABSPATH . 'wp-admin/includes/file.php'; }

        $uploads = \wp_upload_dir();
        if (!is_array($uploads) || empty($uploads['basedir'])) {
            return ['exists'=>false, 'downloading'=>false, 'message'=>'uploads not ready'];
        }
        $fontBase = rtrim($uploads['basedir'], '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts';
        if (!is_dir($fontBase)) { @wp_mkdir_p($fontBase); }
        $target = $fontBase . DIRECTORY_SEPARATOR . 'WeiRuanYaHei.ttf';
        if (file_exists($target)) {
            return ['exists'=>true, 'downloading'=>false, 'message'=>'font exists'];
        }

        $lockKey = 'aipost_font_client_check_lock';
        if (!$force && get_transient($lockKey)) {
            // 已在节流窗口内，尝试读取运行中标记
            $running = (bool) get_transient('aipost_font_dl_running');
            return ['exists'=>false, 'downloading'=>$running, 'message'=>$running ? 'downloading' : 'throttled'];
        }

        // 读取候选URL
        $candidates = apply_filters('aipost_font_candidates_wei_ruan_ya_hei', []);
        $single = apply_filters('aipost_default_font_url', get_option('aipost_default_font_url'));
        if (is_string($single) && !empty($single)) { $candidates[] = $single; }
        $candidates = array_values(array_unique(array_filter(array_map('trim', $candidates))));
        if (empty($candidates)) {
            return ['exists'=>false, 'downloading'=>false, 'message'=>'no candidates'];
        }

        $url = $this->select_fastest_url($candidates, 5);
        if (empty($url)) {
            return ['exists'=>false, 'downloading'=>false, 'message'=>'no reachable url'];
        }

        // 仅当确实要发起下载时再设置节流锁（缩短为 3 分钟，便于失败后尽快重试）
        set_transient($lockKey, 1, 3 * MINUTE_IN_SECONDS);

        if (!function_exists('download_url')) { require_once ABSPATH . 'wp-admin/includes/file.php'; }

        // 标记运行中
        set_transient('aipost_font_dl_running', 1, 2 * MINUTE_IN_SECONDS);
        $tmp = download_url($url, 30);
        if (!is_wp_error($tmp) && is_string($tmp) && file_exists($tmp)) {
            @rename($tmp, $target);
            @chmod($target, 0644);
            delete_transient('aipost_font_dl_running');
            error_log('[AI-Post][Runtime] Default font downloaded to: ' . $target);
            return ['exists'=>true, 'downloading'=>false, 'message'=>'downloaded'];
        } else {
            delete_transient('aipost_font_dl_running');
            error_log('[AI-Post][Runtime] Default font download failed: ' . (is_wp_error($tmp) ? $tmp->get_error_message() : 'unknown'));
            if (is_string($tmp) && file_exists($tmp)) { @unlink($tmp); }
            return ['exists'=>false, 'downloading'=>false, 'message'=>'download failed'];
        }
    }

    /**
     * AJAX: 检测字体状态（是否存在/是否下载中）。
     */
    public function ajax_check_font_status(): void
    {
        check_ajax_referer('aipost_upload_font');
        if (!current_user_can('manage_options')) { wp_send_json_error(['message'=>'forbidden']); }

        if (!function_exists('wp_upload_dir')) { require_once ABSPATH . 'wp-includes/functions.php'; }
        $uploads = \wp_upload_dir();
        $fontBase = is_array($uploads) && !empty($uploads['basedir']) ? rtrim($uploads['basedir'], '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts' : '';
        $target = $fontBase ? ($fontBase . DIRECTORY_SEPARATOR . 'WeiRuanYaHei.ttf') : '';
        $exists = $target && file_exists($target);
        $downloading = (bool) get_transient('aipost_font_dl_running');
        wp_send_json_success(['exists'=>$exists, 'downloading'=>$downloading, 'file'=>'WeiRuanYaHei.ttf']);
    }

    /**
     * AJAX: 立即下载默认字体（跳过节流），并返回状态。
     */
    public function ajax_download_default_font_now(): void
    {
        check_ajax_referer('aipost_upload_font');
        if (!current_user_can('manage_options')) { wp_send_json_error(['message'=>'forbidden']); }
        $res = $this->attempt_default_font_download(true);
        if ($res['exists']) {
            wp_send_json_success(['message'=>'默认字体已就绪']);
        }
        if (!empty($res['downloading'])) {
            wp_send_json_success(['message'=>'已触发下载，正在进行中']);
        }
        wp_send_json_error(['message'=> $res['message'] ?: '下载失败']);
    }

    /**
     * 在候选URL中挑选最快可达的地址（简单以 HEAD 延迟作为参考）。
     * @param array $urls
     * @param int $timeout
     * @return string 最优URL或空
     */
    private function select_fastest_url(array $urls, int $timeout = 5): string
    {
        $fastest = '';
        $best = PHP_INT_MAX;
        foreach ($urls as $u) {
            $t0 = microtime(true);
            $resp = wp_remote_head($u, ['timeout' => $timeout]);
            if (is_wp_error($resp)) { continue; }
            $code = wp_remote_retrieve_response_code($resp);
            if ($code >= 200 && $code < 400) {
                $dt = (int) round((microtime(true) - $t0) * 1000);
                if ($dt < $best) { $best = $dt; $fastest = $u; }
            }
        }
        return $fastest;
    }

    /**
     * 处理字体上传：将 ttf/otf/ttc 保存至 uploads/aipost-fonts
     */
    public function ajax_upload_font(): void
    {
        if (!current_user_can('manage_options')) { wp_send_json_error(['message' => '权限不足'], 403); }
        check_admin_referer('aipost_upload_font');

        if (empty($_FILES['font_file']) || !isset($_FILES['font_file']['tmp_name'])) {
            wp_send_json_error(['message' => '未选择文件'], 400);
        }

        $file = $_FILES['font_file'];
        $name = isset($file['name']) ? $file['name'] : '';
        if (!preg_match('/\.(ttf|otf|ttc)$/i', $name)) {
            wp_send_json_error(['message' => '仅支持 .ttf/.otf/.ttc'], 400);
        }

        if (!function_exists('wp_upload_dir')) {
            require_once ABSPATH . 'wp-includes/functions.php';
        }
        if (!function_exists('wp_mkdir_p')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        $uploads = \wp_upload_dir();
        if (!is_array($uploads) || empty($uploads['basedir'])) {
            wp_send_json_error(['message' => 'uploads 不可用'], 500);
        }
        $fontBase = rtrim($uploads['basedir'], '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts';
        if (!is_dir($fontBase)) { @wp_mkdir_p($fontBase); }

        $target = $fontBase . DIRECTORY_SEPARATOR . basename($name);
        $tmp = $file['tmp_name'];
        if (!@move_uploaded_file($tmp, $target)) {
            // 某些环境下 move_uploaded_file 可能失败，尝试重命名
            if (!@rename($tmp, $target)) {
                wp_send_json_error(['message' => '保存失败，请检查目录权限'], 500);
            }
        }
        @chmod($target, 0644);
        wp_send_json_success(['file' => basename($name)]);
    }

    /**
     * 主站点菜单
     */
    public function admin_menu(): void {
        \add_menu_page(
            \esc_html__( '极光AI', 'ai-post' ),
            \esc_html__( '极光AI', 'ai-post' ), 
            'manage_options', 
            'ai-post',  
            array( $this, 'display_plugin_page' ),
            \plugins_url('icon.png', __FILE__),
            99                                
        );
    }
    
    /**
     * 子站点菜单
     */
    public function site_admin_menu(): void {
        if (!is_main_site() && is_multisite()) {
            \add_menu_page(
                \esc_html__( '极光AI', 'ai-post' ),
                \esc_html__( '极光AI', 'ai-post' ), 
                'manage_options', 
                'ai-post',  
                array( $this, 'display_plugin_page' ),
                \plugins_url('icon.png', __FILE__),
                99                                
            );
        }
    }

    /**
     * 移除冗余子菜单
     */
    public function remove_submenu(): void {
        // 移除设置页面下的菜单，因为我们已经有了主菜单
        \remove_submenu_page('options-general.php', 'ai-post');
    }

    /**
     * 显示插件页面
     */
    public function display_plugin_page(): void {
        echo '<h1>' . \esc_html__( 'AI自动写文章设置', 'ai-post' ) . '</h1>';
    }

    /**
     * 添加插件操作链接
     * @param array $links 现有链接
     * @return array 修改后的链接
     */
    public function plugin_action_links($links)
    {
        // 确定正确的设置URL
        if (is_network_admin()) {
            // 网络管理界面 - 链接到网络设置页面
            $settings_url = network_admin_url('admin.php?page=ai-post-network');
        } else {
            // 子站点或单站点 - 链接到站点设置页面
            $settings_url = admin_url('admin.php?page=ai-post');
        }
        
        // 添加设置链接到操作链接列表
        $settings_link = '<a href="' . esc_url($settings_url) . '">' . __('设置', 'ai-post') . '</a>';
        array_unshift($links, $settings_link);
        
        return $links;
    }

    /**
     * 在插件设置页按需加载前端脚本（安全保存令牌）
     */
    public function enqueue_admin_assets($hook = ''): void
    {
        // 仅在本插件主页面加载
        $isTarget = false;
        if (isset($_GET['page']) && $_GET['page'] === 'ai-post') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            $isTarget = true;

            // 性能优化：若 CGPS 仍尝试加载外链 CodeMirror（入队顺序/缓存等原因），在最终输出阶段强制替换为本地资源
            static $aipost_codemirror_filters_added = false;
            if (!$aipost_codemirror_filters_added) {
                $aipost_codemirror_filters_added = true;
                \add_filter('style_loader_src', array($this, 'filter_cgps_codemirror_style_src'), 9999, 2);
                \add_filter('script_loader_src', array($this, 'filter_cgps_codemirror_script_src'), 9999, 2);
            }

            // 性能优化：移除 CGPS 框架加载的外链 Font Awesome（CDN 慢会导致白屏）
            if (function_exists('wp_dequeue_style')) {
                \wp_dequeue_style('cgps-fa5');
                \wp_dequeue_style('cgps-fa5-v4-shims');
                \wp_dequeue_style('cgps-fa');
            }
            if (function_exists('wp_deregister_style')) {
                \wp_deregister_style('cgps-fa5');
                \wp_deregister_style('cgps-fa5-v4-shims');
                \wp_deregister_style('cgps-fa');
            }

            // 先注册并注入配置：安全令牌保存基础脚本（供内联脚本依赖）
            $handle = 'ai-post-secure-token';
            $src    = \plugins_url('resources/js/secure-token.js', __FILE__);
            \wp_register_script($handle, $src, array(), HAOZI_AIPOST_VERSION, true);
            $restBase = \esc_url_raw( \get_rest_url() );
            $nonce    = \wp_create_nonce('wp_rest');
            \wp_localize_script($handle, 'AIPostSecureConfig', array(
                'restBase' => $restBase,
                'nonce'    => $nonce,
            ));
            \wp_enqueue_script($handle);

            // 内联表单改造脚本：拦截含有 account-secret-key 的字段，改走 REST 安全保存
            \wp_register_script('ai-post-secure-token-inline', \plugins_url('resources/js/secure-token-inline.js', __FILE__), array('ai-post-secure-token'), HAOZI_AIPOST_VERSION, true);
            \wp_enqueue_script('ai-post-secure-token-inline');

            // 安妮聊天：后台抽屉 UI
            $chatCss = \plugins_url('resources/css/admin-chat.css', __FILE__);
            \wp_enqueue_style('ai-post-admin-chat', $chatCss, array(), HAOZI_AIPOST_VERSION);

            $chatJs = \plugins_url('resources/js/admin-chat.js', __FILE__);
            \wp_register_script('ai-post-admin-chat', $chatJs, array(), HAOZI_AIPOST_VERSION, true);
            \wp_localize_script('ai-post-admin-chat', 'AIPostAdminChatConfig', array(
                'restBase' => \esc_url_raw(\get_rest_url()),
                'nonce' => \wp_create_nonce('wp_rest'),
                'ajaxUrl' => \admin_url('admin-ajax.php'),
                'ajaxNonce' => \wp_create_nonce('aipost_admin_chat'),
                'iconUrl' => \plugins_url('robot.png', __FILE__),
            ));
            \wp_enqueue_script('ai-post-admin-chat');
        }

    }

    public function filter_cgps_codemirror_style_src($src, $handle) {
        if ($handle !== 'cgps-codemirror') {
            return $src;
        }
        if (!isset($_GET['page']) || $_GET['page'] !== 'ai-post') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return $src;
        }
        return \plugins_url('assets/css/codemirror.min.css', __FILE__);
    }

    public function filter_cgps_codemirror_script_src($src, $handle) {
        if (!isset($_GET['page']) || $_GET['page'] !== 'ai-post') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return $src;
        }
        if ($handle === 'cgps-codemirror') {
            $p = \plugin_dir_path(__FILE__) . 'assets/js/codemirror.min.js';
            if (\file_exists($p)) {
                return \plugins_url('assets/js/codemirror.min.js', __FILE__);
            }
        }
        if ($handle === 'cgps-codemirror-loadmode') {
            $p = \plugin_dir_path(__FILE__) . 'assets/js/loadmode.min.js';
            if (\file_exists($p)) {
                return \plugins_url('assets/js/loadmode.min.js', __FILE__);
            }
        }
        return $src;
    }

    public function render_admin_chat_entry(): void
    {
        if (!isset($_GET['page']) || $_GET['page'] !== 'ai-post') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }

        echo '<div id="aipost-admin-chat-root"></div>';
    }

    public function render_admin_chat_root(): void
    {
        if (!isset($_GET['page']) || $_GET['page'] !== 'ai-post') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }

        $robotIcon = \esc_url(\plugins_url('robot.png', __FILE__));

        echo '<div id="aipost-admin-chat-overlay"></div>';
        echo '<div id="aipost-admin-chat-drawer">'
            . '<div class="aipost-admin-chat-resize-handle nw" data-dir="nw"></div>'
            . '<div class="aipost-admin-chat-resize-handle ne" data-dir="ne"></div>'
            . '<div class="aipost-admin-chat-resize-handle sw" data-dir="sw"></div>'
            . '<div class="aipost-admin-chat-resize-handle se" data-dir="se"></div>'
            . '<div class="aipost-admin-chat-topbar">'
            . '<div class="left"><div class="brand"><img class="aipost-admin-chat-brand-icon" src="'. $robotIcon .'" alt="安妮" /></div><div class="hint">安妮</div></div>'
            . '<div class="right"><select id="aipost-admin-chat-model"></select></div>'
            . '<button type="button" class="button" id="aipost-admin-chat-close">关闭</button>'
            . '</div>'
            . '<div class="aipost-admin-chat-main">'
            . '<div id="aipost-admin-chat-sessions"></div>'
            . '<div id="aipost-admin-chat-chat">'
            . '<div id="aipost-admin-chat-messages"></div>'
            . '<div class="aipost-admin-chat-status" id="aipost-admin-chat-status"></div>'
            . '<div class="aipost-admin-chat-composer">'
            . '<textarea id="aipost-admin-chat-input" placeholder="输入消息...（Enter 发送   Ctrl + Enter换行）"></textarea>'
            . '<button type="button" class="button button-primary" id="aipost-admin-chat-send">发送</button>'
            . '</div>'
            . '</div>'
            . '</div>'
            . '</div>'
            . '</div>';
    }

    /**
     * 在设置页底部渲染“令牌快速保存(REST)”面板
     */
    public function render_secure_token_panel(): void
    {
        // 面板迁移说明：
        // 根据用户反馈，“通信令牌（Cron/REST）”的使用说明与操作统一迁移至
        // 全局设定 -> 系统调试与日志（由 Service\Setting::render_debug_log_panel 渲染）。
        // 此方法保留以兼容旧钩子，但不再输出任何内容，避免重复显示与位置不一致。
        if (!isset($_GET['page']) || $_GET['page'] !== 'ai-post') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }
        return;
        ?>
        <div class="notice" style="background:#f8fafc;border:1px solid #e5e7eb;border-radius:8px;padding:12px;margin-top:16px;">
            <h2 style="margin:0 0 8px;">令牌快速保存（REST JSON-only）</h2>
            <p style="margin:4px 0 10px;color:#555;">通过零杂音 REST 路由保存敏感令牌，可显著降低 WAF/CDN 注入导致的报错弹窗概率。</p>
            <div style="display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;">
                <div>
                    <label>OpenAI 兼容 key</label><br>
                    <input type="password" id="aipost-tk-openai" style="min-width:300px;" placeholder="sk-..." />
                    <button type="button" class="button button-primary" id="aipost-btn-openai">保存</button>
                </div>
                <div>
                    <label>自定义（custom）key</label><br>
                    <input type="password" id="aipost-tk-custom" style="min-width:300px;" placeholder="sk-..." />
                    <button type="button" class="button" id="aipost-btn-custom">保存</button>
                </div>
                <div>
                    <label>豆包（doubao）key</label><br>
                    <input type="password" id="aipost-tk-doubao" style="min-width:280px;" placeholder="volc-..." />
                    <button type="button" class="button" id="aipost-btn-doubao">保存</button>
                </div>
                <div>
                    <label>阿里百炼（bai-lian）key</label><br>
                    <input type="password" id="aipost-tk-bailian" style="min-width:280px;" placeholder="sk-..." />
                    <button type="button" class="button" id="aipost-btn-bailian">保存</button>
                </div>
                <div>
                    <label>DeepSeek key</label><br>
                    <input type="password" id="aipost-tk-deepseek" style="min-width:260px;" placeholder="sk-..." />
                    <button type="button" class="button" id="aipost-btn-deepseek">保存</button>
                </div>
                <div>
                    <label>文心（wenxin）apiKey / secretKey</label><br>
                    <input type="text" id="aipost-tk-wenxin-ak" style="width:200px;" placeholder="apiKey" />
                    <span>：</span>
                    <input type="password" id="aipost-tk-wenxin-sk" style="width:200px;" placeholder="secretKey" />
                    <button type="button" class="button" id="aipost-btn-wenxin">保存</button>
                </div>
            </div>
            <div id="aipost-tk-msg" style="margin-top:10px;color:#111;"></div>
        </div>
        <?php
        ?>

        <script>
        (function(){
            function setMsg(s, ok){
                var el = document.getElementById('aipost-tk-msg');
                if(!el) return;
                el.style.color = ok ? '#065f46' : '#991b1b';
                el.textContent = s;
            }
            function bind(btnId, getter){
                var btn = document.getElementById(btnId);
                if(!btn) return;
                btn.addEventListener('click', async function(){
                    try {
                        var payload = getter();
                        if (!window.AIPostSecure || !AIPostSecure.saveToken) {
                            throw new Error('前端工具未初始化');
                        }
                        btn.disabled = true; btn.textContent = '保存中...';
                        var res = await AIPostSecure.saveToken(payload);
                        setMsg('保存成功', true);
                    } catch (e) {
                        setMsg((e && e.message) ? e.message : '保存失败', false);
                    } finally {
                        btn.disabled = false; btn.textContent = '保存';
                    }
                });
            }
            bind('aipost-btn-openai', function(){
                return { k: 'openai_key', v: String(document.getElementById('aipost-tk-openai').value || '') };
            });
            bind('aipost-btn-custom', function(){
                return { k: 'custom_key', v: String(document.getElementById('aipost-tk-custom').value || '') };
            });
            bind('aipost-btn-doubao', function(){
                return { k: 'doubao_key', v: String(document.getElementById('aipost-tk-doubao').value || '') };
            });
            bind('aipost-btn-bailian', function(){
                return { k: 'bailian_key', v: String(document.getElementById('aipost-tk-bailian').value || '') };
            });
            bind('aipost-btn-deepseek', function(){
                return { k: 'deepseek_key', v: String(document.getElementById('aipost-tk-deepseek').value || '') };
            });
            bind('aipost-btn-wenxin', function(){
                var ak = String(document.getElementById('aipost-tk-wenxin-ak').value || '');
                var sk = String(document.getElementById('aipost-tk-wenxin-sk').value || '');
                return { k: 'wenxin_key', v: ak + ':' + sk };
            });

            // （移除通信令牌卡片的内联JS，逻辑已迁移到 Service/Setting.php）
        })();
        </script>
        <?php
        // 追加：标题图字体挂载提示面板（方式二）
        try {
            if (function_exists('wp_upload_dir')) {
                $uploads = wp_upload_dir();
                $basedir = isset($uploads['basedir']) ? $uploads['basedir'] : '';
                $baseurl = isset($uploads['baseurl']) ? $uploads['baseurl'] : '';
                $fontDir = rtrim($basedir, '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts';
                $fontUrl = rtrim($baseurl, '/\\') . '/aipost-fonts';
                $detected = [];
                if (is_dir($fontDir)) {
                    $files = @scandir($fontDir);
                    if (is_array($files)) {
                        foreach ($files as $f) {
                            if ($f === '.' || $f === '..') continue;
                            if (preg_match('/\.(ttf|otf|ttc)$/i', $f)) {
                                $detected[] = $f;
                            }
                            if (count($detected) >= 10) break; // 限制展示数量
                        }
                    }
                }
                ?>
                <div class="notice notice-info" style="margin-top:20px;padding:12px 15px;">
                    <h3 style="margin:0 0 8px;">
                        标题图字体挂载（方式二）
                    </h3>
                    <p style="margin:6px 0;">
                        建议将字体文件（如 <code>NotoSansSC-Regular.ttf</code>、<code>WeiRuanYaHei.ttf</code>）放置到：
                        <br><code><?php echo esc_html($fontDir); ?></code>
                    </p>
                    <p style="margin:6px 0;">
                        该目录对应 URL：<a href="<?php echo esc_attr($fontUrl); ?>" target="_blank"><?php echo esc_html($fontUrl); ?></a>
                    </p>
                    <p style="margin:6px 0;">
                        当前检测到的字体（最多列出 10 个）：
                        <?php if ($detected) { ?>
                            <code><?php echo esc_html(implode(', ', $detected)); ?></code>
                        <?php } else { ?>
                            <em>未检测到字体文件</em>
                        <?php } ?>
                    </p>
                    <p style="margin:6px 0;">
                        开启“配图方式二（标题图）”时，可在任务中填写字体文件名，例如 <code>WeiRuanYaHei.ttf</code>；若未找到字体，将自动回退为纯色背景方案，不会中断发布。
                    </p>
                    <p style="margin:6px 0;color:#555;">
                        如需在激活时自动下载默认字体，可通过 <code>add_filter('aipost_default_font_url', ...)</code> 或设置 <code>option: aipost_default_font_url</code> 指定下载地址（注意版权合规）。
                    </p>
                </div>
                <?php
            }
        } catch (\Throwable $e) {
            // 静默
        }
    }

    /**
     * 确保 uploads/aipost-fonts 目录与 README 存在（幂等）。
     * 目的：让升级用户在不执行停用/启用的情况下也能就绪方式二字体目录。
     */
    private function ensure_font_mount_dir(): void
    {
        if (!function_exists('wp_upload_dir')) {
            require_once ABSPATH . 'wp-includes/functions.php';
        }
        if (!function_exists('wp_mkdir_p')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        $uploads = \wp_upload_dir();
        if (!is_array($uploads) || empty($uploads['basedir'])) {
            return;
        }
        $fontBase = rtrim($uploads['basedir'], '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts';
        $created = false;
        if (!is_dir($fontBase)) {
            @wp_mkdir_p($fontBase);
            $created = is_dir($fontBase);
        }
        $readme = $fontBase . DIRECTORY_SEPARATOR . 'README.txt';
        if (!file_exists($readme)) {
            @file_put_contents($readme, "This directory is used by ai-post (Title Image - Method 2) to load font files.\r\n"
                . "Place your preferred fonts here, e.g. NotoSansSC-Regular.ttf or WeiRuanYaHei.ttf.\r\n");
        }
        // 记录日志，便于确认目录状态
        if ($created) {
            error_log('[AI-Post][Bootstrap] Created font mount dir: ' . $fontBase);
        } else {
            error_log('[AI-Post][Bootstrap] Font mount dir present: ' . $fontBase);
        }
    }

    /**
     * 设置页顶部提示：展示字体挂载目录与已检测字体（最多10条）
     */
    private function render_font_mount_notice(): void
    {
        if (!function_exists('wp_upload_dir')) {
            require_once ABSPATH . 'wp-includes/functions.php';
        }
        $uploads = \wp_upload_dir();
        $basedir = isset($uploads['basedir']) ? $uploads['basedir'] : '';
        $baseurl = isset($uploads['baseurl']) ? $uploads['baseurl'] : '';
        $fontDir = rtrim($basedir, '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts';
        $fontUrl = rtrim($baseurl, '/\\') . '/aipost-fonts';
        $detected = [];
        if (is_dir($fontDir)) {
            $files = @scandir($fontDir);
            if (is_array($files)) {
                foreach ($files as $f) {
                    if ($f === '.' || $f === '..') continue;
                    if (preg_match('/\.(ttf|otf|ttc)$/i', $f)) {
                        $detected[] = $f;
                    }
                    if (count($detected) >= 10) break;
                }
            }
        }
        ?>
        <div class="notice notice-info" style="margin-top:10px;padding:12px 15px;">
            <h3 style="margin:0 0 8px;">标题图字体挂载（方式二）</h3>
            <p style="margin:6px 0;">将字体文件（如 <code>NotoSansSC-Regular.ttf</code>、<code>WeiRuanYaHei.ttf</code>）放置到：<br>
                <code><?php echo esc_html($fontDir); ?></code>
            </p>
            <p style="margin:6px 0;">对应 URL：<a href="<?php echo esc_attr($fontUrl); ?>" target="_blank"><?php echo esc_html($fontUrl); ?></a></p>
            <p style="margin:6px 0;">当前检测到的字体（最多 10 个）：
                <?php if ($detected) { ?>
                    <code><?php echo esc_html(implode(', ', $detected)); ?></code>
                <?php } else { ?>
                    <em>未检测到字体文件</em>
                <?php } ?>
            </p>
            <p style="margin:6px 0;">任务启用“方式二（标题图）”时，填写文件名如 <code>WeiRuanYaHei.ttf</code>；未命中将自动回退纯色背景，不中断发布。</p>
        </div>
        <?php
    }

    /**
     * 检测版本升级并在本次升级中仅尝试一次默认字体下载（避免重复下载）。
     * 条件：
     * - 站点配置/过滤器提供了默认字体 URL（option: aipost_default_font_url 或 filter: aipost_default_font_url）
     * - uploads/aipost-fonts/WeiRuanYaHei.ttf 不存在
     * - 存储的上次下载标记版本 != 当前版本
     */
    private function maybe_download_default_font_once(): void
    {
        // 读取版本与标记
        $currentVersion = defined('HAOZI_AIPOST_VERSION') ? HAOZI_AIPOST_VERSION : '0.0.0';
        $doneKey = 'aipost_font_dl_done_version';
        $doneVer = get_option($doneKey, '');

        // 若已在当前版本尝试过，则跳过
        if (is_string($doneVer) && $doneVer === $currentVersion) {
            return;
        }

        if (!function_exists('wp_upload_dir')) {
            require_once ABSPATH . 'wp-includes/functions.php';
        }
        if (!function_exists('wp_mkdir_p')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        $uploads = \wp_upload_dir();
        if (!is_array($uploads) || empty($uploads['basedir'])) {
            update_option($doneKey, $currentVersion, false);
            return;
        }
        $fontBase = rtrim($uploads['basedir'], '/\\') . DIRECTORY_SEPARATOR . 'aipost-fonts';
        if (!is_dir($fontBase)) {
            @wp_mkdir_p($fontBase);
        }
        $target = $fontBase . DIRECTORY_SEPARATOR . 'WeiRuanYaHei.ttf';
        if (file_exists($target)) {
            update_option($doneKey, $currentVersion, false);
            return; // 已存在，不需下载
        }

        // 读取候选URL：优先 filter 列表，其次单一 option/filter
        $candidates = apply_filters('aipost_font_candidates_wei_ruan_ya_hei', []);
        $single = apply_filters('aipost_default_font_url', get_option('aipost_default_font_url'));
        if (is_string($single) && !empty($single)) { $candidates[] = $single; }
        $candidates = array_values(array_unique(array_filter(array_map('trim', $candidates))));
        if (empty($candidates)) {
            update_option($doneKey, $currentVersion, false);
            return;
        }

        // 选择最快可达的 URL
        $url = $this->select_fastest_url($candidates, 5);
        if (empty($url)) {
            update_option($doneKey, $currentVersion, false);
            return;
        }

        if (!function_exists('download_url')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $tmp = download_url($url, 30);
        if (!is_wp_error($tmp) && is_string($tmp) && file_exists($tmp)) {
            // 将临时文件移动到目标
            @rename($tmp, $target);
            @chmod($target, 0644);
            error_log('[AI-Post][Upgrade] Default font downloaded once to: ' . $target);
            update_option($doneKey, $currentVersion, false);
        } else {
            error_log('[AI-Post][Upgrade] Default font download failed (upgrade-once): ' . (is_wp_error($tmp) ? $tmp->get_error_message() : 'unknown'));
            if (is_string($tmp) && file_exists($tmp)) { @unlink($tmp); }
            // 仍然标记已尝试，避免每次加载都发起下载；管理员可手动删除该标记后重试
            update_option($doneKey, $currentVersion, false);
        }
    }

    public function enqueue_frontend_assets(): void
    {
        if (!\function_exists('is_singular') || !\is_singular()) {
            return;
        }

		static $aipost_front_css_added = false;
		if ($aipost_front_css_added) {
			return;
		}
		$aipost_front_css_added = true;

		$icon = \esc_url_raw(\plugins_url('Service/assets/images/icon.png', PLUGIN_FILE));
		$css = ".ai-post-ai-summary .ai-post-ai-summary-title{display:flex!important;align-items:center!important;justify-content:flex-start!important;gap:6px!important;flex-wrap:nowrap!important;white-space:nowrap!important;word-break:keep-all!important;text-align:left!important;float:none!important;position:static!important;}\n"
			. ".ai-post-ai-summary .ai-post-ai-summary-title .ai-post-ai-summary-icon{display:inline-block!important;width:18px!important;height:18px!important;flex:0 0 18px!important;background:transparent url('{$icon}') no-repeat center/contain!important;float:none!important;position:static!important;}\n"
			. ".ai-post-ai-summary .ai-post-ai-summary-title .ai-post-ai-summary-title-text{display:inline!important;}";

		\add_action('wp_head', function() use ($css) {
			echo "\n<style id=\"ai-post-frontend-inline\">\n{$css}\n</style>\n";
		}, 999);
	}
}
