use super::Highlighter;
use pulldown_cmark::escape::escape_html;
use std::fmt::Write;
use tree_sitter_highlight::{Highlight as TSHighlight, HighlightConfiguration, HighlightEvent};

pub struct TreeSitterHighlighter {
    config: &'static HighlightConfiguration,
}

// SAFETY: the only thing we store is a HighlightConfiguration, which is thread safe
// unsafe impl Sync for TreeSitterHighlighter {}

impl TreeSitterHighlighter {
    pub fn new(config: &'static HighlightConfiguration) -> Self {
        Self { config }
    }
}

impl Highlighter for TreeSitterHighlighter {
    fn highlight(&self, code: &str) -> String {
        let mut highlighter = tree_sitter_highlight::Highlighter::new();
        let highlights = highlighter
            .highlight(self.config, code.as_bytes(), None, |_| None)
            .unwrap();
        let mut buf = String::new();
        for highlight in highlights {
            let event = highlight.unwrap();
            match event {
                HighlightEvent::Source { start, end } => {
                    escape_html(
                        &mut buf,
                        std::str::from_utf8(&code.as_bytes()[start..end]).unwrap(),
                    )
                    .unwrap();
                }
                HighlightEvent::HighlightStart(TSHighlight(idx)) => {
                    write!(
                        &mut buf,
                        "<span class=\"hl-{}\">",
                        HIGHLIGHT_CLASS_NAMES[idx]
                    )
                    .unwrap();
                }
                HighlightEvent::HighlightEnd => {
                    buf.push_str("</span>");
                }
            }
        }
        buf
    }
}

pub const HIGHLIGHT_NAMES: &[&str] = &[
    "attribute",
    "comment",
    "constant",
    "embedded", // something inside a string, e.g., the foo in `${foo}` in js
    "function",
    "function.builtin",
    "keyword",
    "module", // e.g., atoms in elixir
    "number",
    "operator",
    "property",
    "punctuation.special", // e.g., in js the dollar sign and curly braces in `${foo}`
    "string",
    "string.special.key", // e.g., json keys
    "tag",                // e.g., html tags, css class/id selectors
    "type",
    "type.builtin",
    "variable",
    "variable.builtin",
];

const HIGHLIGHT_CLASS_NAMES: &[&str] = &[
    "attr", "cmt", "const", "emb", "fn", "builtin", "kw", "mod", "num", "op", "prop", "punct-sp",
    "str", "key", "tag", "type", "builtin", "var", "builtin",
];
