<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;

if (!defined('MIRAI_CORE_READY')) {
    define('MIRAI_CORE_READY', true);
}

require_once __DIR__ . '/Sitemap/Service.php';
require_once __DIR__ . '/Sitemap/Action.php';
require_once __DIR__ . '/Orders/Action.php';
require_once __DIR__ . '/Withdrawals/Action.php';
require_once __DIR__ . '/Links/Action.php';
require_once __DIR__ . '/Wechat/Action.php';
require_once __DIR__ . '/About.php';
require_once __DIR__ . '/Balance/Action.php';
require_once __DIR__ . '/Crawler/Service.php';
require_once __DIR__ . '/Crawler/Hook.php';

/**
 * Mirai未来主题核心
 * 
 * 处理主题及系统级权限的功能，是Mirai未来主题的必要核心组件。
 * 
 * @package MiraiCore
 * @author 苏酷伊 Sukuy
 * @version 1.0.2
 * @link https://www.sukuy.com
 */

class MiraiCore_Plugin implements Typecho_Plugin_Interface
{
    const MIRAI_FIELDS = ['cover', 'views', 'excerpt', 'keywords', 'description'];

    public static function activate()
    {
        Typecho_Plugin::factory('Widget_Upload')->uploadHandle = array('MiraiCore_Plugin', 'uploadHandle');
        Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('MiraiCore_Plugin', 'modifyHandle');

        Typecho_Plugin::factory('Widget_Contents_Post_Edit')->fields = array('MiraiCore_Plugin', 'interceptFields');
        Typecho_Plugin::factory('Widget_Contents_Page_Edit')->fields = array('MiraiCore_Plugin', 'interceptFields');

        Typecho_Plugin::factory('Widget_Contents_Post_Edit')->finishSave = array('MiraiCore_Plugin', 'handleSaveFields');
        Typecho_Plugin::factory('Widget_Contents_Page_Edit')->finishSave = array('MiraiCore_Plugin', 'handleSaveFields');
        Typecho_Plugin::factory('Widget_Contents_Post_Edit')->finishPublish = array('MiraiCore_Plugin', 'handleSaveFields');
        Typecho_Plugin::factory('Widget_Contents_Page_Edit')->finishPublish = array('MiraiCore_Plugin', 'handleSaveFields');

        Typecho_Plugin::factory('index.php')->begin = array('MiraiCore_Plugin', 'interceptIndexBegin');
        Typecho_Plugin::factory('Widget_Archive')->footer = array('MiraiCore_Crawler_Hook', 'handle');
        Typecho_Plugin::factory('Widget_Logout')->action = array('MiraiCore_Plugin', 'handleLogout');

        Typecho_Plugin::factory('admin/header.php')->header = array('MiraiCore_Plugin', 'renderHeader');
        Typecho_Plugin::factory('admin/footer.php')->end = array('MiraiCore_Plugin', 'renderFooter');
        Typecho_Plugin::factory('admin/common.php')->begin = array('MiraiCore_Plugin', 'handleAdminBegin');

        $miraiMenuIndex = Helper::addMenu('Mirai');
        Helper::addPanel($miraiMenuIndex, 'MiraiCore/Orders/manage-orders.php', '订单管理', '管理订单', 'administrator');
        Helper::addPanel($miraiMenuIndex, 'MiraiCore/Withdrawals/manage-withdrawals.php', '提现管理', '管理提现申请', 'administrator');
        Helper::addPanel($miraiMenuIndex, 'MiraiCore/Links/manage-links.php', '友情链接', '管理友情链接', 'administrator');
        Helper::addPanel($miraiMenuIndex, 'MiraiCore/Links/manage-categories.php', '链接分类', '管理链接分类', 'administrator');
        Helper::addPanel($miraiMenuIndex, 'MiraiCore/Sitemap/manage-sitemap.php', '网站地图', '管理网站地图', 'administrator');
        Helper::addPanel($miraiMenuIndex, 'MiraiCore/Crawler/manage-crawler.php', '爬虫记录', '查看爬虫访问记录', 'administrator');
        Helper::addAction('links-submit', 'MiraiCore_Links_Action');
        Helper::addAction('miraicrawler-process', 'MiraiCore_Crawler_Action');
        Helper::addAction('miraicorerepairdb', 'MiraiCore_RepairDb_Action');
        Helper::addAction('miraicoresitemap', 'MiraiCore_Sitemap_Action');
        Helper::addAction('mirai-orders', 'MiraiCore_Orders_Action');
        Helper::addAction('mirai-withdrawals', 'MiraiCore_Withdrawals_Action');
        Helper::addAction('mirai-links', 'MiraiCore_Links_Batch_Action');
        Helper::addAction('mirai-wechat', 'MiraiCore_Wechat_Action');
        
        self::loadThemeFile('migration.php', 'common/mysql');
        if (function_exists('Mirai_checkDatabase')) {
            Mirai_checkDatabase();
        }

        return _t('Mirai 核心组件已激活');
    }

    private static function getSchemaSnapshot()
    {
        $snapshot = [];
        try {
            $db = Typecho_Db::get();
            $prefix = $db->getPrefix();
            $tables = $db->fetchAll($db->query("SHOW TABLES LIKE '" . $prefix . "%'"));
            foreach ($tables as $row) {
                $tableName = '';
                foreach ($row as $value) {
                    $tableName = (string)$value;
                    break;
                }
                if ($tableName === '') {
                    continue;
                }
                try {
                    $columns = $db->fetchAll($db->query('SHOW COLUMNS FROM `' . $tableName . '`'));
                } catch (Exception $e) {
                    continue;
                }
                $columnNames = [];
                foreach ($columns as $column) {
                    if (!isset($column['Field'])) {
                        continue;
                    }
                    $columnNames[] = (string)$column['Field'];
                }
                sort($columnNames);
                $snapshot[$tableName] = $columnNames;
            }
        } catch (Exception $e) {
        }
        ksort($snapshot);
        return $snapshot;
    }

    private static function summarizeSchemaChanges($before, $after)
    {
        $changes = [];
        foreach ($after as $tableName => $columns) {
            if (!isset($before[$tableName])) {
                $changes[] = '新建表：' . $tableName;
                continue;
            }
            $newColumns = array_values(array_diff($columns, $before[$tableName]));
            sort($newColumns);
            foreach ($newColumns as $columnName) {
                $changes[] = '新增字段：' . $tableName . '.' . $columnName;
            }
        }
        return $changes;
    }

    public static function renderFooter()
    {
        require_once __DIR__ . '/VIP/Action.php';
        MiraiCore_VIP_Action::renderFooter();
        MiraiCore_Balance_Action::renderFooter();
    }

    public static function renderHeader($header)
    {
        $cssUrl = defined('__TYPECHO_PLUGIN_URL__') ? __TYPECHO_PLUGIN_URL__ . '/MiraiCore/assets/css/mirai.css' : '/usr/plugins/MiraiCore/assets/css/mirai.css';
        return $header . '<link rel="stylesheet" type="text/css" href="' . $cssUrl . '" />';
    }

    public static function deactivate()
    {
        $panelTable = Helper::options()->panelTable;
        $parentMenus = empty($panelTable['parent']) ? [] : $panelTable['parent'];
        $menuIndex = array_search('Mirai', $parentMenus);
        
        if ($menuIndex !== false) {
            if (isset($panelTable['child'][$menuIndex + 10])) {
                unset($panelTable['child'][$menuIndex + 10]);
            }
            unset($panelTable['parent'][$menuIndex]);
            Helper::setOption('panelTable', $panelTable);
        }
        
        Helper::removeAction('links-submit');
        Helper::removeAction('miraicrawler-process');
        Helper::removeAction('miraicorerepairdb');
        Helper::removeAction('miraicoresitemap');
        Helper::removeAction('mirai-orders');
        Helper::removeAction('mirai-withdrawals');
        Helper::removeAction('mirai-links');
        Helper::removeAction('mirai-wechat');
    }
    
    public static function config(Typecho_Widget_Helper_Form $form){}

    public static function personalConfig(Typecho_Widget_Helper_Form $form){}

    public static function handleLogout()
    {
        $user = Typecho_Widget::widget('Widget_User');
        $user->logout();
        @session_destroy();
        header('Location: /');
        exit;
    }

    public static function interceptIndexBegin()
    {
        require_once __DIR__ . '/VIP/Action.php';
        MiraiCore_VIP_Action::interceptIndexBegin();
        MiraiCore_Balance_Action::interceptIndexBegin();

        self::interceptAuth();
    }

    public static function interceptAuth()
    {
        if (MiraiCore_Sitemap_Service::maybeHandleRequest()) {
            return;
        }
        $script = isset($_SERVER['SCRIPT_NAME']) ? (string)$_SERVER['SCRIPT_NAME'] : '';
        $adminPath = '/' . trim((string)__TYPECHO_ADMIN_DIR__, '/');
        $isLoginPage = stripos($script, $adminPath . '/login.php') !== false;
        $isRegisterPage = stripos($script, $adminPath . '/register.php') !== false;
        if (!$isLoginPage && !$isRegisterPage) {
            return;
        }
        try {
            $options = Typecho_Widget::widget('Widget_Options');
            $user = Typecho_Widget::widget('Widget_User');
            if ($user->pass('administrator', true)) {
                return;
            }
            $themeOptions = isset($options->theme) ? $options->{$options->theme} : null;
            $userCenterEnabled = !isset($themeOptions->enableUserCenter) || $themeOptions->enableUserCenter === '1';
            $frontendLoginEnabled = !isset($themeOptions->enableFrontendLogin) || $themeOptions->enableFrontendLogin === '1';
            if ($isLoginPage && $userCenterEnabled && $frontendLoginEnabled) {
                return;
            }
            if ($isRegisterPage && $options->allowRegister) {
                return;
            }
            header('Location: /');
            exit;
        } catch (Exception $e) {
        }
    }

    public static function handleAdminBegin()
    {
        self::interceptAdminAccess();
        self::checkDatabaseOnAdminLogin();
        self::renderAdminScript();
        self::renderWechatAssets();
    }

    public static function checkDatabaseOnAdminLogin()
    {
        try {
            $user = Typecho_Widget::widget('Widget_User');
            
            // 只对管理员执行检查
            if (!$user->pass('administrator', true)) {
                return;
            }
            
            $options = Typecho_Widget::widget('Widget_Options');
            $lastCheckTime = $options->get('mirai_last_db_check_time');
            $currentTime = time();
            $checkInterval = 24 * 60 * 60; // 24小时
            
            // 检查是否需要执行数据库检查
            if (!$lastCheckTime || ($currentTime - $lastCheckTime > $checkInterval)) {
                // 使用锁机制防止并发执行
                $lockKey = 'mirai_db_check_lock';
                $lockTime = $options->get($lockKey);
                
                // 如果锁存在且未超时（5分钟），跳过检查
                if ($lockTime && ($currentTime - $lockTime <= 300)) {
                    return;
                }
                
                // 设置锁
                $options->set($lockKey, $currentTime);
                
                try {
                    // 加载主题的数据库迁移文件
                    self::loadThemeFile('migration.php', 'common/mysql');
                    
                    if (function_exists('Mirai_checkDatabase')) {
                        Mirai_checkDatabase();
                    }
                    
                    // 更新最后检查时间
                    $options->set('mirai_last_db_check_time', $currentTime);
                } catch (Exception $e) {
                    // 记录错误但不影响后台使用
                    error_log('Mirai database check failed: ' . $e->getMessage());
                } finally {
                    // 释放锁
                    $options->set($lockKey, 0);
                }
            }
        } catch (Exception $e) {
            // 静默失败，不影响后台使用
            error_log('Mirai checkDatabaseOnAdminLogin error: ' . $e->getMessage());
        }
    }

    public static function interceptAdminAccess()
    {
        if (PHP_SAPI === 'cli') {
            return;
        }
        try {
            $options = Typecho_Widget::widget('Widget_Options');
            $user = Typecho_Widget::widget('Widget_User');
            if ($user->pass('administrator', true)) {
                return;
            }
            $themeOptions = isset($options->theme) ? $options->{$options->theme} : null;
            $userCenterEnabled = !isset($themeOptions->enableUserCenter) || $themeOptions->enableUserCenter === '1';
            if ($userCenterEnabled) {
                return;
            }
            $script = isset($_SERVER['SCRIPT_NAME']) ? (string)$_SERVER['SCRIPT_NAME'] : '';
            $adminPath = '/' . trim((string)__TYPECHO_ADMIN_DIR__, '/');
            if (stripos($script, $adminPath . '/') === false) {
                return;
            }
            $entry = basename($script);
            if ($entry === 'login.php' || $entry === 'register.php') {
                header('Location: /');
                exit;
            }
            if (!$user->hasLogin()) {
                header('Location: /');
                exit;
            }
            header('Location: /');
            exit;
        } catch (Exception $e) {
        }
    }

    public static function renderAdminScript()
    {
        $adminPath = '/' . trim((string)__TYPECHO_ADMIN_DIR__, '/');
        if (PHP_SAPI !== 'cli' && isset($_SERVER['SCRIPT_NAME']) && strpos((string)$_SERVER['SCRIPT_NAME'], $adminPath . '/') !== false) {
            $options = Typecho_Widget::widget('Widget_Options');
            $authorQq = MiraiCore_About::getAuthorQq();
            $security = Typecho_Widget::widget('Widget_Security');
            $repairToken = $security->getToken('miraicorerepairdb');
            $redirect = isset($_SERVER['REQUEST_URI']) ? (string)$_SERVER['REQUEST_URI'] : $adminPath . '/';
            $actionBase = rtrim((string)$options->index, '/');
            if ($actionBase === '' || strpos($actionBase, 'index.php') === false) {
                $actionBase = rtrim((string)$options->siteUrl, '/') . '/index.php';
            }
            $repairUrl = $actionBase . '/action/miraicorerepairdb?_token=' . rawurlencode($repairToken) . '&redirect=' . rawurlencode($redirect) . '&ajax=1';
            $themeVersion = MiraiCore_About::getThemeVersion();

            $builder = '(function(){' .
                'window.MIRAI_CORE_REPAIR_DB_URL=\'' . $repairUrl . '\';' .
                'window.MIRAI_CORE_HEADER_BUILDER=function(){' .
                'return \'<div class="mirai-config-header"><div class="mirai-header-right"><span class="mirai-badge is-gray"><i class="ri-information-line"></i> 当前版本 v' . $themeVersion . '</span><a href="javascript:;" class="mirai-badge is-green js-mirai-repair-db"><i class="ri-database-2-line"></i> 修复数据表</a><span class="mirai-badge is-purple"><i class="ri-qq-line"></i> QQ：' . $authorQq . '</span></div></div><p class="mirai-config-tip"></p>\';' .
                '};' .
            '})();';
            echo '<script>' . $builder . '</script>';
        }
    }

    public static function renderWechatAssets()
    {
        MiraiCore_Wechat_Action::renderAssets();
    }

    public static function repairDatabaseStructures()
    {
        $before = self::getSchemaSnapshot();
        // 加载主题迁移文件（包含 mirai_links 等所有表定义）
        self::loadThemeFile('migration.php', 'common/mysql');
        if (function_exists('Mirai_checkDatabase')) {
            Mirai_checkDatabase();
        }
        $after = self::getSchemaSnapshot();
        return self::summarizeSchemaChanges($before, $after);
    }

    public static function loadThemeFile($file, $subdir = '')
    {
        try {
            $options = Typecho_Widget::widget('Widget_Options');
            $themeDir = $options->theme;
            $themeBase = __TYPECHO_ROOT_DIR__ . '/usr/themes/' . $themeDir . '/';

            if (!empty($subdir)) {
                $path = $themeBase . $subdir . '/' . $file;
            } else {
                $path = $themeBase . $file;
            }
            
            if (file_exists($path)) {
                require_once $path;
                return true;
            }
        } catch (Exception $e) {
        }
        
        return false;
    }

    public static function uploadHandle($file)
    {
        if (self::loadThemeFile('upload.php', 'modules') && function_exists('Mirai_uploadHandle')) {
            return Mirai_uploadHandle($file);
        }
        return false;
    }

    public static function modifyHandle($content, $file)
    {
        if (self::loadThemeFile('upload.php', 'modules') && function_exists('Mirai_modifyHandle')) {
            return Mirai_modifyHandle($content, $file);
        }
        return false;
    }

    private static function sanitize($value, $allowHtml = false)
    {
        if (empty($value)) {
            return '';
        }

        $value = trim((string)$value);

        if ($allowHtml) {
            return $value;
        }

        return htmlspecialchars(strip_tags($value), ENT_QUOTES | ENT_HTML5, 'UTF-8');
    }

    private static function saveEdkData($cid, $fields)
    {
        if (empty($cid) || empty($fields) || !is_array($fields)) {
            return;
        }

        $db = Typecho_Db::get();
        $prefix = $db->getPrefix();
        $edkTable = $prefix . 'mirai_contents_edk';

        $edkData = [
            'excerpt' => self::sanitize($fields['excerpt'] ?? '', true),
            'keywords' => self::sanitize($fields['keywords'] ?? ''),
            'description' => self::sanitize($fields['description'] ?? '', true)
        ];

        if (empty(array_filter($edkData))) {
            return;
        }

        try {
            $exists = $db->fetchRow($db->select('cid')->from($edkTable)->where('cid = ?', $cid));

            if ($exists) {
                $db->query($db->update($edkTable)->rows($edkData)->where('cid = ?', $cid));
            } else {
                $edkData['cid'] = $cid;
                $db->query($db->insert($edkTable)->rows($edkData));
            }
        } catch (Exception $e) {
        }
    }

    private static function saveContentsExtensions($cid, $fields)
    {
        if (empty($cid) || empty($fields) || !is_array($fields)) {
            return;
        }

        $db = Typecho_Db::get();
        $updateData = [];

        if (isset($fields['cover']) && is_string($fields['cover'])) {
            $updateData['cover'] = self::sanitize($fields['cover']);
        }

        if (isset($fields['views']) && is_numeric($fields['views'])) {
            $updateData['views'] = max(0, intval($fields['views']));
        }

        if (!empty($updateData)) {
            try {
                $db->query($db->update('table.contents')->rows($updateData)->where('cid = ?', $cid));
            } catch (Exception $e) {
            }
        }
    }

    private static function cleanupTypechoFields($cid)
    {
        if (empty($cid)) {
            return;
        }

        try {
            $db = Typecho_Db::get();
            $db->query($db->delete('table.fields')
                ->where('cid = ?', $cid)
                ->where('name IN ?', self::MIRAI_FIELDS));
        } catch (Exception $e) {
        }
    }

    public static function interceptFields($fields, $widget)
    {
        $filteredFields = [];
        foreach ($fields as $name => $value) {
            if (!in_array($name, self::MIRAI_FIELDS)) {
                $filteredFields[$name] = $value;
            }
        }

        return $filteredFields;
    }

    public static function handleSaveFields($contents, $widget = NULL)
    {
        if ($widget === NULL) {
            $widget = $contents;
        }

        $cid = $widget->cid ?? 0;

        if ($cid <= 0) {
            return $contents;
        }

        $fields = $widget->request->getArray('fields') ?? [];

        if (!empty($fields)) {
            self::saveContentsExtensions($cid, $fields);
            self::saveEdkData($cid, $fields);
            self::cleanupTypechoFields($cid);
        }
        MiraiCore_Sitemap_Service::markDirty();
        MiraiCore_Sitemap_Service::autoBuildIfNeeded(false);

        self::triggerSeoPush($contents, $widget);

        return $contents;
    }

    public static function triggerSeoPush($contents, $widget)
    {
        self::loadThemeFile('functions.php', 'common');
        if (function_exists('Mirai_seoPushOnSave')) {
            Mirai_seoPushOnSave($contents, $widget);
        }
    }

    public static function buildAbout()
    {
        return MiraiCore_About::build();
    }
}

class MiraiCore_Links_Action extends Typecho_Widget implements Widget_Interface_Do
{
    public function action()
    {
        $this->submit();
    }

    public function submit()
    {
        try {
            $token = trim((string)$this->request->get('_token'));
            $security = Typecho_Widget::widget('Widget_Security');
            $expectedToken = $security->getToken('links-submit');
            $tokenValid = function_exists('hash_equals') ? hash_equals($expectedToken, $token) : ($expectedToken === $token);
            if ($token === '' || !$tokenValid) {
                $this->response->throwJson(array('success' => false, 'message' => '请求验证失败，请刷新页面后重试'));
            }
            $linkName = trim((string)$this->request->get('linkName'));
            $linkUrl = trim((string)$this->request->get('linkUrl'));
            $linkImage = trim((string)$this->request->get('linkImage'));
            $linkCategory = intval($this->request->get('linkCategory', 0));
            $linkDescription = trim((string)$this->request->get('linkDescription'));
            if ($linkName === '' || $linkUrl === '') {
                $this->response->throwJson(array('success' => false, 'message' => '请填写必填项'));
            }
            if (!filter_var($linkUrl, FILTER_VALIDATE_URL)) {
                $this->response->throwJson(array('success' => false, 'message' => '网站地址格式不正确'));
            }
            $db = Typecho_Db::get();
            $prefix = $db->getPrefix();
            $exists = $db->fetchRow($db->select()
                ->from($prefix . 'mirai_links')
                ->where('url = ?', $linkUrl));
            if ($exists) {
                $this->response->throwJson(array('success' => false, 'message' => '该链接已存在'));
            }
            $options = Typecho_Widget::widget('Widget_Options');
            $defaultVisible = 'N';
            $submitLimit = isset($options->linksSubmitLimit) ? intval($options->linksSubmitLimit) : 300;
            $clientIp = '';
            if (method_exists($this->request, 'getIp')) {
                $clientIp = (string)$this->request->getIp();
            } elseif (isset($_SERVER['REMOTE_ADDR'])) {
                $clientIp = (string)$_SERVER['REMOTE_ADDR'];
            }
            if ($clientIp === '') {
                $clientIp = 'guest';
            }
            if ($submitLimit > 0) {
                $cacheKey = 'mirai_links_submit_' . md5($clientIp);
                $lastSubmit = intval(Typecho_Cookie::get($cacheKey));
                if ($lastSubmit > 0 && (time() - $lastSubmit) < $submitLimit) {
                    $this->response->throwJson(array('success' => false, 'message' => '提交过于频繁，请稍后再试'));
                }
            }
            if ($linkCategory > 0) {
                $existsCategory = $db->fetchRow($db->select('mid')
                    ->from($prefix . 'metas')
                    ->where('type = ?', 'link_category')
                    ->where('mid = ?', $linkCategory)
                    ->limit(1));
                if (!$existsCategory) {
                    $linkCategory = 0;
                }
            }
            $insertData = array(
                'name' => $linkName,
                'url' => $linkUrl,
                'image' => $linkImage,
                'description' => $linkDescription,
                'category' => $linkCategory,
                'visible' => $defaultVisible,
                'created' => time(),
                'updated' => time()
            );
            $db->query($db->insert($prefix . 'mirai_links')->rows($insertData));
            if ($submitLimit > 0) {
                Typecho_Cookie::set($cacheKey, strval(time()), $submitLimit);
            }
            $message = '提交成功，等待审核';
            $this->response->throwJson(array(
                'success' => true, 
                'message' => $message
            ));
        } catch (Throwable $e) {
            $message = '提交失败，请重试';
            if (defined('__TYPECHO_DEBUG__') && __TYPECHO_DEBUG__) {
                $message = '提交失败：' . $e->getMessage();
            }
            $this->response->throwJson(array('success' => false, 'message' => $message));
        }
    }
}

class MiraiCore_RepairDb_Action extends Typecho_Widget implements Widget_Interface_Do
{
    private $bufferLevel = 0;

    private function returnResult($ok, $message, $redirect, $details = [])
    {
        $isAjax = trim((string)$this->request->get('ajax')) === '1';
        if ($isAjax) {
            while (ob_get_level() > $this->bufferLevel) {
                ob_end_clean();
            }
            $this->response->throwJson([
                'success' => $ok,
                'message' => $message,
                'details' => $details
            ]);
            return;
        }
        $glue = strpos($redirect, '?') === false ? '?' : '&';
        $flag = $ok ? 'ok' : 'fail';
        $target = $redirect . $glue . 'mirai_db_repair=' . $flag . '&mirai_db_repair_msg=' . rawurlencode($message);
        $this->response->redirect($target);
    }

    public function action()
    {
        $this->bufferLevel = ob_get_level();
        ob_start();
        $options = Typecho_Widget::widget('Widget_Options');
        $pluginsUrl = $options->adminUrl('plugins.php');
        $security = Typecho_Widget::widget('Widget_Security');
        $token = trim((string)$this->request->get('_token'));
        $expectedNew = $security->getToken('miraicorerepairdb');
        $expectedOld = $security->getToken('mirai-core-repair-db');
        if (function_exists('hash_equals')) {
            $valid = hash_equals($expectedNew, $token) || hash_equals($expectedOld, $token);
        } else {
            $valid = ($expectedNew === $token) || ($expectedOld === $token);
        }
        $redirect = trim((string)$this->request->get('redirect'));
        $adminPath = '/' . trim((string)__TYPECHO_ADMIN_DIR__, '/');
        if (strpos($redirect, $adminPath . '/') !== 0) {
            $redirect = $adminPath . '/options-theme.php';
        }
        if ($token === '' || !$valid) {
            $this->returnResult(false, '请求验证失败', $redirect, []);
            return;
        }
        $user = Typecho_Widget::widget('Widget_User');
        if (!$user->pass('administrator', true)) {
            $this->returnResult(false, '权限不足', $pluginsUrl, []);
            return;
        }
        try {
            $changes = MiraiCore_Plugin::repairDatabaseStructures();
            if (empty($changes)) {
                $message = '数据库结构已是最新，无需修复';
            } else {
                $message = '数据库结构补齐完成：' . implode('；', $changes);
            }
            $this->returnResult(true, $message, $redirect, $changes);
        } catch (Throwable $e) {
            $this->returnResult(false, '数据库修复失败：' . $e->getMessage(), $redirect, []);
        }
    }
}