//! 构建时工具：从 Catppuccin .tmTheme 主题文件生成 `public/highlight.css`。
//!
//! 该二进制文件在编译/构建阶段运行（通过 `make highlight-css` 或
//! `cargo run --bin generate_highlight_css`），使用 syntect 解析 Sublime Text
//! 的 `.tmTheme` XML 主题定义，并输出供博客正文代码块使用的 CSS 类规则。
//!
//! 主要流程：
//! 1. 从 `themes/` 目录加载 Catppuccin Latte（浅色）与 Mocha（深色）两套 tmTheme；
//! 2. 调用 syntect 的 `css_for_theme_with_class_style` 生成基于类名的 CSS；
//! 3. 去掉 syntect 自带的注释以减小体积；
//! 4. 将选择器重写为以 `.md-content pre code` 为作用域，深色模式再加 `.dark ` 前缀；
//! 5. 写入 `public/highlight.css`，供运行时 Markdown 渲染引用。
//!
//! CSS 类前缀规则：
//! - 浅色主题：`{base} { ... }` 与 `{base} .kw { ... }`；
//! - 深色主题：`.dark {base} { ... }` 与 `.dark {base} .kw { ... }`；
//! - 其中 `{base}` 为 `.md-content pre code`，保证只影响正文代码块；
//! - `.code` 是 syntect 的根作用域特殊类，只映射到 base 容器本身。
//!
//! 背景色策略：syntect 输出的 `background-color` 会被剥离，代码块背景统一由
//! `input.css` 中的 `--color-paper-code-block` 变量提供（亮色浅、暗色深），
//! 避免 `.code` span 与外层 `pre` 双层背景叠加导致的边缘色差。

use syntect::highlighting::ThemeSet;
use syntect::html::{css_for_theme_with_class_style, ClassStyle};

fn main() {
    // 加载 Catppuccin Latte（浅色）与 Mocha（深色）Sublime Text tmTheme 文件
    let latte = ThemeSet::get_theme("themes/Catppuccin Latte.tmTheme")
        .expect("Failed to load Catppuccin Latte theme");
    let mocha = ThemeSet::get_theme("themes/Catppuccin Mocha.tmTheme")
        .expect("Failed to load Catppuccin Mocha theme");

    // 使用 Spaced 类风格生成 CSS，例如 `.kw`、`.kw .kw2`
    // 这样每个 token 作用域会生成独立的 class，便于精确控制高亮颜色
    let latte_css = css_for_theme_with_class_style(&latte, ClassStyle::Spaced)
        .expect("Failed to generate Latte CSS");
    let mocha_css = css_for_theme_with_class_style(&mocha, ClassStyle::Spaced)
        .expect("Failed to generate Mocha CSS");

    // 重写选择器：浅色主题无前缀，深色主题加 `.dark ` 前缀
    let latte_rewritten = rewrite_rules(&latte_css, ".md-content pre code", "");
    let mocha_rewritten = rewrite_rules(&mocha_css, ".md-content pre code", ".dark ");

    let mut output = String::new();
    output.push_str("/* Auto-generated by generate_highlight_css — DO NOT EDIT */\n");
    output.push_str("/* Catppuccin Latte (light) */\n");
    output.push_str(&latte_rewritten);
    output.push_str("/* Catppuccin Mocha (dark) */\n");
    output.push_str(&mocha_rewritten);

    // 压缩最终 CSS：注释、空白、换行统一在这一步处理（minify_css 会跳过 /* */）
    let output = minify_css(&output);

    // 确保 public/ 目录存在并写入生成的 CSS
    std::fs::create_dir_all("public").expect("Failed to create public/");
    std::fs::write("public/highlight.css", output).expect("Failed to write public/highlight.css");

    println!("Generated public/highlight.css");
}

/// 压缩 CSS：移除注释、合并空白、删除选择器/属性周围的无用空格。
fn minify_css(css: &str) -> String {
    let mut out = String::with_capacity(css.len());
    let chars: Vec<char> = css.chars().collect();
    let mut i = 0;
    let mut last_significant: Option<char> = None;
    let mut pending_space = false;

    while i < chars.len() {
        // 跳过 /* ... */ 注释
        if i + 1 < chars.len() && chars[i] == '/' && chars[i + 1] == '*' {
            i += 2;
            while i + 1 < chars.len() && !(chars[i] == '*' && chars[i + 1] == '/') {
                i += 1;
            }
            i += 2;
            continue;
        }

        let ch = chars[i];
        if ch.is_whitespace() {
            if last_significant.is_some() {
                pending_space = true;
            }
            i += 1;
            continue;
        }

        // 在特定分隔符前后不需要空格
        let no_space_before = "{ } : ; ,".contains(ch);
        let no_space_after = last_significant.is_some_and(|c| "{ } : ; ,".contains(c));

        if pending_space && !no_space_after && !no_space_before {
            out.push(' ');
        }
        pending_space = false;

        out.push(ch);
        last_significant = Some(ch);
        i += 1;
    }

    out.trim().to_string()
}

/// 从 CSS 规则体中移除 `background-color` 声明。
///
/// syntect 会为根作用域 `.code` 注入主题底色（如 Latte `#eff1f5`），这与
/// 外层 `pre` 的 `--color-paper-code-block` 背景叠加会形成色差。这里只保留
/// 前景色相关声明，让背景由单一来源控制。
fn strip_background_color(body: &str) -> String {
    body.split(';')
        .filter(|decl| {
            let trimmed = decl.trim();
            !trimmed.is_empty() && !trimmed.to_ascii_lowercase().starts_with("background-color")
        })
        .collect::<Vec<_>>()
        .join(";")
}

/// 剥离 CSS 字符串开头的 `/* ... */` 注释块（含其后的空白）。
///
/// syntect 输出的第一条规则选择器前会带主题头注释，这里在 `.code` 特判前
/// 先清掉它，使根作用域类能被正确识别并映射到 base 容器本身。
fn strip_leading_comment(s: &str) -> &str {
    let trimmed = s.trim_start();
    if trimmed.starts_with("/*") {
        if let Some(end) = trimmed.find("*/") {
            return trimmed[end + 2..].trim_start();
        }
    }
    s
}

/// 将 syntect 生成的 CSS 选择器包装到指定的 base 选择器下，并可附加前缀。
///
/// syntect 原始输出形如：
///   .code { ... }
///   .kw { color: #xxx; }
///   .kw, .kt { color: #yyy; }
///
/// 转换后变为：
///   .md-content pre code { ... }
///   .dark .md-content pre code .kw { ... }
///
/// 参数：
/// - `css`: syntect 生成的原始 CSS；
/// - `base`: 作用域基础选择器，这里固定为 `.md-content pre code`；
/// - `prefix`: 主题前缀，浅色主题传空字符串，深色主题传 `.dark `。
fn rewrite_rules(css: &str, base: &str, prefix: &str) -> String {
    let mut out = String::new();
    let mut pos = 0;
    let bytes = css.as_bytes();

    while pos < bytes.len() {
        let rest = &css[pos..];

        let open = match rest.find('{') {
            Some(i) => i,
            None => break,
        };

        let selector = rest[..open].trim();
        if selector.is_empty() {
            pos += open + 1;
            continue;
        }
        // syntect 会把主题头注释（/* theme ... */）粘在第一条规则的选择器前。
        // 注释虽在后续 minify 阶段会去除，但此处的 .code 特判发生在 minify 之前，
        // 所以这里先剥掉前导注释块，避免根作用域类匹配失败。
        let selector = strip_leading_comment(selector);

        // 查找配对的右大括号，处理可能的嵌套 {}
        let after_open = pos + open + 1;
        let mut depth = 1usize;
        let mut close = after_open;
        while close < bytes.len() && depth > 0 {
            if bytes[close] == b'{' {
                depth += 1;
            } else if bytes[close] == b'}' {
                depth -= 1;
            }
            close += 1;
        }

        let body = css[after_open..close - 1].trim();
        pos = close;

        if body.is_empty() {
            continue;
        }

        // 剥离 syntect 注入的 background-color，代码块背景改由
        // `--color-paper-code-block` 变量统一提供（见 input.css）。
        let body = strip_background_color(body);
        if body.is_empty() {
            continue;
        }

        // .code 是 syntect 用于根作用域的特殊类，直接映射到 base 容器
        if selector == ".code" {
            out.push_str(&format!("{}{} {{\n{}\n}}\n\n", prefix, base, body));
        } else {
            // 对每个逗号分隔的选择器前加上 prefix + base，保持多选择器规则完整
            let rewritten = selector
                .split(',')
                .map(|s| format!("{}{} {}", prefix, base, s.trim()))
                .collect::<Vec<_>>()
                .join(",\n");
            out.push_str(&format!("{} {{\n{}\n}}\n\n", rewritten, body));
        }
    }

    out
}
