<?php

/**
 * EnhancedMarkdown - 增强版Markdown解析器插件
 *
 * 基于 Parsedown + ParsedownExtra 解析引擎，支持更全面的 Markdown 语法。
 * 完全替换系统默认 HyperDown 解析器，从根本上解决语法支持不足和显示故障问题。
 *
 * 支持的语法：
 * - 标准 Markdown：标题/加粗/斜体/链接/图片/代码/列表/引用/分割线
 * - GFM 扩展：表格（完整）、删除线 ~~text~~、任务列表 - [x] / - [ ]
 * - Extra 扩展：脚注 [^1]、定义列表、缩写词 *[ABBR]: definition
 * - 自定义扩展：上标 ^text^、下标 ~text~、高亮 ==text==
 * - 自定义扩展：TOC [toc]、容器 :::type、图片尺寸 ![alt](url =WxH)
 * - 自定义扩展：数学公式 $...$ 和 $$...$$（KaTeX 前端渲染）
 * - 自定义扩展：Mermaid 图表 ```mermaid（Mermaid.js 前端渲染）
 *
 * @package EnhancedMarkdown
 * @author 首政君
 * @version 3.5.3
 * @link https://blog.qgdnmb.xyz/2026/05/26/Typecho-Markdown.html
 */

namespace TypechoPlugin\EnhancedMarkdown;

use Typecho\Plugin\PluginInterface;
use Typecho\Widget\Helper\Form;
use Typecho\Widget\Helper\Form\Element\Radio;
use Widget\Options;

if (!defined('__TYPECHO_ROOT_DIR__')) {
    exit;
}

// 修复 #4：将 require_once 移至安全检查之后，避免在未验证环境下加载外部文件
// Parsedown 解析引擎（在命名空间外加载，确保全局可用）
require_once __DIR__ . '/Parsedown.php';
require_once __DIR__ . '/ParsedownExtra.php';

/**
 * EnhancedMarkdown 插件主入口类
 *
 * 注册 markdown hook 替换系统默认解析器，
 * 同时注册 header hook 注入增强语法CSS样式。
 */
class Plugin implements PluginInterface
{
    /**
     * 插件资产文件基础路径（相对于插件目录）
     *
     * KaTeX 和 Mermaid 的 JS/CSS 文件存放于此目录下。
     *
     * @since v3.3.0 新增
     */
    const ASSETS_SUBDIR = 'usr/plugins/EnhancedMarkdown';

    /**
     * 按需加载标志：当前请求是否包含数学公式元素
     *
     * 在 parse() 中检测解析结果是否包含 .math-block / .math-inline 元素，
     * injectScripts() 据此决定是否加载 KaTeX 资源。
     * 每次请求自动重置（PHP 请求生命周期）。
     *
     * @since v3.4.0 新增
     * @var bool
     */
    private static $hasMath = false;

    /**
     * 按需加载标志：当前请求是否包含 Mermaid 图表元素
     *
     * 在 parse() 中检测解析结果是否包含 class="mermaid" 元素，
     * injectScripts() 据此决定是否加载 Mermaid.js 资源。
     * 每次请求自动重置（PHP 请求生命周期）。
     *
     * @since v3.4.0 新增
     * @var bool
     */
    private static $hasMermaid = false;

    /**
     * 激活插件
     *
     * 注册以下 Hook：
     * - Widget\Base\Contents → markdown：替换文章 Markdown 解析器
     * - Widget\Base\Comments → markdown：替换评论 Markdown 解析器
     * - Widget\Archive → header：注入增强语法 CSS 样式
     * - Widget\Archive → footer：注入 KaTeX/Mermaid JS 脚本
     *
     * @static
     * @access public
     * @return void
     */
    public static function activate()
    {
        // 修复 #8：Hook 注册改用数组格式（IDE 支持跳转、静态分析工具可检测）
        // 文章 Markdown 解析 hook（替换默认 Markdown::convert）
        \Typecho\Plugin::factory('Widget\Base\Contents')->markdown = [__CLASS__, 'parse'];

        // 评论 Markdown 解析 hook（替换默认 Markdown::convert）
        \Typecho\Plugin::factory('Widget\Base\Comments')->markdown = [__CLASS__, 'parse'];

        // 自定义 CSS 输出到文章页面头部
        \Typecho\Plugin::factory('Widget\Archive')->header = [__CLASS__, 'injectStyles'];

        // JS 脚本输出到文章页面底部（KaTeX、Mermaid 按需加载）
        \Typecho\Plugin::factory('Widget\Archive')->footer = [__CLASS__, 'injectScripts'];
    }

    /**
     * 停用插件
     *
     * @static
     * @access public
     * @return void
     */
    public static function deactivate()
    {
        // 停用时自动移除所有钩子（Typecho机制）
    }

    /**
     * 插件配置面板
     *
     * @access public
     * @param Form $form 配置表单
     * @return void
     */
    public static function config(Form $form)
    {
        // TOC目录开关
        $toc = new Radio(
            'toc',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('TOC目录支持'),
            _t('启用后可使用 [toc] 标记自动生成文章目录，标题自动添加锚点ID')
        );
        $form->addInput($toc);

        // 任务列表开关
        $taskList = new Radio(
            'taskList',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('任务列表支持'),
            _t('启用后支持 - [x] 和 - [ ] 语法生成复选框列表')
        );
        $form->addInput($taskList);

        // 上标开关
        $sup = new Radio(
            'sup',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('上标语法支持'),
            _t('启用后支持 ^text^ 语法生成上标 <sup>text</sup>')
        );
        $form->addInput($sup);

        // 下标开关
        $sub = new Radio(
            'sub',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('下标语法支持'),
            _t('启用后支持 ~text~ 语法生成下标 <sub>text</sub>（注意与删除线 ~~text~~ 的区别）')
        );
        $form->addInput($sub);

        // 高亮标记开关
        $mark = new Radio(
            'mark',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('高亮标记支持'),
            _t('启用后支持 ==text== 语法生成高亮标记 <mark>text</mark>')
        );
        $form->addInput($mark);

        // 容器/提示块开关
        $container = new Radio(
            'container',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('容器/提示块支持'),
            _t('启用后支持 :::tip、:::info、:::warning、:::danger、:::note、:::success 等容器块语法')
        );
        $form->addInput($container);

        // 图片尺寸开关
        $imageSize = new Radio(
            'imageSize',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('图片尺寸支持'),
            _t('启用后支持 ![alt](url =WxH) 语法设置图片宽高')
        );
        $form->addInput($imageSize);

        // 数学公式开关
        $math = new Radio(
            'math',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('数学公式支持（KaTeX）'),
            _t('启用后支持 $...$ 行内公式和 $$...$$ 块级公式，由 KaTeX 前端渲染')
        );
        $form->addInput($math);

        // Mermaid 图表开关
        $mermaid = new Radio(
            'mermaid',
            array('1' => _t('启用'), '0' => _t('禁用')),
            '1',
            _t('Mermaid 图表支持'),
            _t('启用后支持 ```mermaid 代码块渲染流程图、时序图、甘特图等')
        );
        $form->addInput($mermaid);

        // 标题 ID 格式开关（仅在 TOC 目录启用时生效）
        // v3.5.2 新增：允许管理员在 slug 格式（可读、可预测）和旧版数字格式（toc-N）之间切换
        // 关闭 TOC 目录后此选项无效（标题不会生成任何 ID）
        $slugId = new Radio(
            'slugId',
            array('1' => _t('Slug 格式（推荐）'), '0' => _t('数字格式（toc-N）')),
            '1',
            _t('标题锚点 ID 格式'),
            _t('仅在 TOC 目录启用时生效。Slug 格式根据标题文本生成可读 ID（如 "hello-world"、"安装步骤"），便于分享和锚点链接；数字格式按解析顺序生成 toc-1、toc-2 等。关闭 TOC 后标题不生成任何 ID，此选项无效')
        );
        $form->addInput($slugId);
    }

    /**
     * 个人用户的配置面板（本插件无此需求）
     *
     * @access public
     * @param Form $form
     * @return void
     */
    public static function personalConfig(Form $form)
    {
        // 无个人配置项
    }

    /**
     * 解析 Markdown 文本
     *
     * 挂载到 markdown hook，替换系统默认的 Markdown::convert()。
     * markdown hook 签名：filter('markdown', $text)
     * Typecho filter() 会传递 ($text, $lastResult) 两个参数。
     *
     * @static
     * @access public
     * @param string|null $text Markdown 原始文本
     * @param string|null $lastResult 上一个 filter 的返回值（修复 #6：补全签名）
     * @return string 解析后的 HTML
     */
    public static function parse($text, $lastResult = null)
    {
        require_once __DIR__ . '/Parser.php';

        // 修复 #5：使用静态缓存避免每次调用新建 Parser 实例
        // 首页等多文章列表页面对同一请求可复用实例，减少构造函数开销
        static $parser;
        if (empty($parser)) {
            $parser = new Parser();
        }

        // 每次调用时读取最新配置并重置 TOC 状态
        $options = Options::alloc()->plugin('EnhancedMarkdown');

        $parser->configure(array(
            'toc'       => $options->toc ?: '0',
            'taskList'  => $options->taskList ?: '0',
            'sup'       => $options->sup ?: '0',
            'sub'       => $options->sub ?: '0',
            'mark'      => $options->mark ?: '0',
            'container' => $options->container ?: '0',
            'imageSize' => $options->imageSize ?: '0',
            'math'      => $options->math ?: '0',
            'mermaid'   => $options->mermaid ?: '0',
            'slugId'    => $options->slugId ?: '0',
        ));

        // 初始化解析器（启用HTML、注册Hook）
        $parser->init();

        $html = $parser->makeHtml($text);

        // 按需加载检测：扫描解析结果中是否包含数学公式和 Mermaid 图表元素
        // 设置静态标志供 injectStyles() / injectScripts() 读取，
        // 避免在不含公式/图表的页面加载 KaTeX（~1.5MB）或 Mermaid（3.2MB）
        // @since v3.4.0 新增
        if ($options->math !== '0') {
            self::$hasMath = self::$hasMath
                || strpos($html, 'class="math-block"') !== false
                || strpos($html, 'class="math-inline"') !== false;
        }
        if ($options->mermaid !== '0') {
            self::$hasMermaid = self::$hasMermaid
                || strpos($html, 'class="mermaid"') !== false;
        }

        // 修复 #3：兼容系统 lang- 前缀约定
        // 系统默认 Markdown::convert 会将 <code class="php"> 转为 <code class="lang-php">
        // Parsedown 输出 <code class="language-php">，需转换为 lang- 前缀
        // 以确保现有语法高亮插件（Prism.js、Highlight.js 适配插件）正常工作
        // 修正：正则从 \w+ 改为 [\w-]+，支持含连字符的语言标识（如 objective-c、c++）
        $html = preg_replace(
            '/<code class="language-([\w-]+)"/i',
            '<code class="lang-$1"',
            $html
        );

        // 处理 <!--more--> 标记（与默认 Markdown::convert 行为一致）
        return str_replace(
            '<p><!--more--></p>',
            '<!--more-->',
            $html
        );
    }

    /**
     * 注入增强语法的 CSS 样式
     *
     * 挂载到文章页面头部，输出任务列表、TOC、容器、高亮等增强语法的样式。
     * Archive header hook 通过 call() 调用，传递 ($header, $archive) 参数。
     *
     * @static
     * @access public
     * @param string|null $header 页面 header 内容（修复 #6：补全签名）
     * @param \Widget\Archive|null $archive 当前文章归档对象
     * @return void
     */
    public static function injectStyles($header = null, $archive = null)
    {
        // 增强语法基础样式（轻量，始终输出）
        $css = <<<'STYLE'
<style>
/* EnhancedMarkdown 增强语法样式 */

/* 任务列表 */
.task-list-item {
    list-style: none !important;
    margin-left: -1.5em;
}
.task-list-checkbox {
    margin-right: 0.5em;
    vertical-align: middle;
    position: relative;
    top: -1px;
}

/* TOC目录 */
.toc {
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 6px;
    padding: 15px 20px;
    margin-bottom: 20px;
}
.toc-title {
    font-weight: bold;
    font-size: 1.1em;
    margin: 0 0 10px 0 !important;
    color: #495057;
}
.toc ul {
    list-style: none !important;
    padding-left: 0 !important;
    margin: 0 !important;
}
.toc-list {
    counter-reset: toc-level;
}
.toc-sublist {
    padding-left: 20px !important;
    margin-top: 4px !important;
}
.toc-item {
    padding: 3px 0;
}
.toc-item a {
    color: #0366d6;
    text-decoration: none;
    font-size: 0.95em;
    line-height: 1.6;
}
.toc-item a:hover {
    text-decoration: underline;
}
.toc-h1 { font-weight: bold; }
.toc-h2 { font-weight: 600; }
.toc-h3 { }
.toc-h4 { font-size: 0.9em; color: #666; }

/* 容器/提示块 */
.custom-container {
    padding: 15px 20px;
    border-radius: 6px;
    margin: 16px 0;
    border-left: 4px solid;
}
.custom-container .container-title {
    font-weight: bold;
    margin: 0 0 8px 0 !important;
    font-size: 1em;
}
.custom-container p:last-child {
    margin-bottom: 0;
}
.container-tip {
    background: #e8f5e9;
    border-color: #4caf50;
    color: #2e7d32;
}
.container-tip .container-title { color: #1b5e20; }

.container-info {
    background: #e3f2fd;
    border-color: #2196f3;
    color: #1565c0;
}
.container-info .container-title { color: #0d47a1; }

.container-warning {
    background: #fff8e1;
    border-color: #ff9800;
    color: #e65100;
}
.container-warning .container-title { color: #bf360c; }

.container-danger {
    background: #fce4ec;
    border-color: #f44336;
    color: #c62828;
}
.container-danger .container-title { color: #b71c1c; }

.container-note {
    background: #f3e5f5;
    border-color: #9c27b0;
    color: #6a1b9a;
}
.container-note .container-title { color: #4a148c; }

.container-success {
    background: #e8f5e9;
    border-color: #66bb6a;
    color: #2e7d32;
}
.container-success .container-title { color: #1b5e20; }

.container-details {
    background: #efebe9;
    border-color: #795548;
    color: #4e342e;
}
.container-details .container-title { color: #3e2723; }

.container-quote {
    background: #fafafa;
    border-color: #9e9e9e;
    color: #424242;
}
.container-quote .container-title { color: #212121; }

/* 上标、下标、高亮 */
sup { font-size: 0.75em; vertical-align: super; }
sub { font-size: 0.75em; vertical-align: sub; }
mark {
    background: #fff3a8;
    padding: 1px 4px;
    border-radius: 2px;
}

/* 数学公式块级样式 */
.math-block {
    margin: 1em 0;
    text-align: center;
    overflow-x: auto;
}

/* Mermaid 图表容器 */
.mermaid {
    margin: 1em 0;
    text-align: center;
}

/* 脚注样式 v3.5.2 新增（MISS-2 修复） */
/* ParsedownExtra 生成 .footnotes 容器、.footnote-ref 引用、.footnote-backref 回链 */
/* 独立运行：不依赖主题，确保任何主题下脚注都有可读样式 */
.footnotes {
    margin-top: 2em;
    padding-top: 1em;
    border-top: 1px solid #e1e4e8;
    font-size: 0.9em;
    color: #586069;
}
.footnotes hr {
    display: none;
}
.footnotes ol {
    padding-left: 1.5em;
}
.footnotes li {
    margin-bottom: 0.4em;
    line-height: 1.6;
}
.footnote-ref {
    font-size: 0.85em;
    vertical-align: super;
    line-height: 0;
}
.footnote-ref a {
    color: #0366d6;
    text-decoration: none;
}
.footnote-ref a:hover {
    text-decoration: underline;
}
.footnote-backref {
    font-size: 0.85em;
    color: #0366d6;
    text-decoration: none;
    margin-left: 4px;
}
.footnote-backref:hover {
    text-decoration: underline;
}

/* 代码块标题 v3.5.0 新增 */
/* 支持 ```language title="标题" 语法的标题栏样式 */
.code-block-wrapper {
    margin: 1em 0;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid #e1e4e8;
}
.code-block-wrapper pre {
    margin: 0;
    border-radius: 0;
    border: none;
}
.code-block-title {
    background: #f6f8fa;
    padding: 8px 16px;
    font-size: 0.85em;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    color: #586069;
    border-bottom: 1px solid #e1e4e8;
    font-weight: 500;
}
</style>
STYLE;
        echo $css;

        // 按需加载 KaTeX CSS：仅在页面包含数学公式时输出
        // CSS 必须在 <head> 中加载，否则页面会出现公式闪烁（FOUC）
        // @since v3.4.0 从 injectScripts() 迁移至此，增加按需检测
        if (self::$hasMath) {
            $baseUrl = '/' . self::ASSETS_SUBDIR;
            echo '<link rel="stylesheet" href="' . $baseUrl . '/katex/katex.min.css">' . "\n";
        }
    }

    /**
     * 注入 KaTeX 和 Mermaid 前端渲染脚本
     *
     * 挂载到文章页面底部（footer hook），按配置按需加载：
     * - KaTeX：CSS + JS + auto-render 自动扫描公式标签并渲染
     * - Mermaid：JS + 初始化脚本自动扫描 .mermaid 元素并渲染
     *
     * 资源文件从插件本地目录加载，不依赖外部 CDN。
     *
     * @static
     * @access public
     * @param string|null $footer 页面 footer 内容
     * @param \Widget\Archive|null $archive 当前文章归档对象
     * @return void
     * @since v3.3.0 新增
     */
    public static function injectScripts($footer = null, $archive = null)
    {
        $baseUrl = '/' . self::ASSETS_SUBDIR;

        // KaTeX 数学公式渲染（仅在实际包含公式元素时加载）
        // parse() 已检测页面内容并设置 self::$hasMath 标志
        // KaTeX CSS 已在 injectStyles() 中按需输出到 <head>，此处仅加载 JS
        // @since v3.4.0 改为基于内容的按需加载，避免无公式页面加载 ~1.5MB 资源
        if (self::$hasMath) {
            // KaTeX 核心渲染引擎
            echo '<script defer src="' . $baseUrl . '/katex/katex.min.js"></script>' . "\n";

            // KaTeX auto-render：自动扫描页面中的数学公式标签并渲染
            echo '<script defer src="' . $baseUrl . '/katex/auto-render.min.js"'
                . ' onload="renderMathInElement(document.body, {'
                . 'delimiters: ['
                . '{left: \'\\\\(\', right: \'\\\\)\', display: false},'
                . '{left: \'\\\\[\', right: \'\\\\]\', display: true},'
                . '{left: \'$\', right: \'$\', display: false},'
                . '{left: \'$$\', right: \'$$\', display: true}'
                . '],'
                . 'ignoredTags: [\'script\', \'noscript\', \'style\', \'textarea\', \'pre\', \'code\', \'option\']'
                . '});"'
                . '></script>' . "\n";
        }

        // Mermaid 图表渲染（仅在实际包含图表元素时加载）
        // parse() 已检测页面内容并设置 self::$hasMermaid 标志
        // @since v3.4.0 改为基于内容的按需加载，避免无图表页面加载 3.2MB 资源
        if (self::$hasMermaid) {
            echo '<script src="' . $baseUrl . '/mermaid.min.js"></script>' . "\n";
            echo '<script>mermaid.initialize({startOnLoad:true,theme:\'default\'});</script>' . "\n";
        }
    }
}
