use comrak::plugins::syntect::SyntectAdapterBuilder;
use comrak::{markdown_to_html_with_plugins, Options, Plugins};
use syntect::highlighting::ThemeSet;
use syntect::html::{css_for_theme_with_class_style, ClassStyle};
use syntect::parsing::SyntaxDefinition;

use crate::domain::MarkdownRenderer;

const TYPESCRIPT_SYNTAX: &str =
    include_str!("../../resources/syntaxes/TypeScript.sublime-syntax");

fn build_syntax_set() -> syntect::parsing::SyntaxSet {
    let mut builder = syntect::parsing::SyntaxSet::load_defaults_nonewlines().into_builder();
    if let Ok(def) = SyntaxDefinition::load_from_str(TYPESCRIPT_SYNTAX, true, None) {
        builder.add(def);
    }
    builder.build()
}

pub struct ComrakRenderer;
pub struct ComrakPreviewRenderer;

impl MarkdownRenderer for ComrakRenderer {
    fn render(&self, content: &str) -> String {
        let adapter = SyntectAdapterBuilder::new()
            .syntax_set(build_syntax_set())
            .css()
            .build();
        let mut options = Options::default();
        options.extension.table = true;
        options.extension.strikethrough = true;
        options.extension.autolink = true;
        options.extension.tasklist = true;
        let mut plugins = Plugins::default();
        plugins.render.codefence_syntax_highlighter = Some(&adapter);
        markdown_to_html_with_plugins(content, &options, &plugins)
    }
}

impl MarkdownRenderer for ComrakPreviewRenderer {
    fn render(&self, content: &str) -> String {
        let adapter = SyntectAdapterBuilder::new()
            .syntax_set(build_syntax_set())
            .css()
            .build();
        let options = Options::default();
        let mut plugins = Plugins::default();
        plugins.render.codefence_syntax_highlighter = Some(&adapter);
        markdown_to_html_with_plugins(content, &options, &plugins)
    }
}

pub fn syntax_css_for_theme(syntect_theme_name: &str) -> String {
    let ts = ThemeSet::load_defaults();
    let theme = match ts.themes.get(syntect_theme_name) {
        Some(t) => t,
        None => return String::new(),
    };
    match css_for_theme_with_class_style(theme, ClassStyle::Spaced) {
        Ok(css) => css.replace(".code {", ".syntax-highlighting {"),
        Err(_) => String::new(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn render_outputs_syntax_highlighting_class() {
        let r = ComrakRenderer;
        let html = r.render("```rust\nfn main() {}\n```\n");
        assert!(html.contains("syntax-highlighting"));
    }

    #[test]
    fn render_outputs_classed_spans() {
        let r = ComrakRenderer;
        let html = r.render("```rust\nfn main() {}\n```\n");
        assert!(html.contains("<span class="));
    }

    #[test]
    fn typescript_syntax_is_highlighted() {
        let r = ComrakRenderer;
        let html = r.render("```typescript\ninterface Foo { id: string; }\n```\n");
        assert!(html.contains("<span class="), "TypeScript should produce highlighted spans");
    }

    #[test]
    fn syntax_css_known_theme_contains_wrapper() {
        let css = syntax_css_for_theme("base16-ocean.dark");
        assert!(!css.is_empty());
        assert!(css.contains(".syntax-highlighting {"));
    }

    #[test]
    fn syntax_css_known_theme_contains_token_rules() {
        let css = syntax_css_for_theme("base16-ocean.dark");
        assert!(css.contains("color:") || css.contains("color :"));
    }

    #[test]
    fn syntax_css_unknown_theme_returns_empty() {
        let css = syntax_css_for_theme("nonexistent-theme-xyz");
        assert!(css.is_empty());
    }
}
