use std::path::{Path, PathBuf};

use comrak::plugins::syntect::SyntectAdapterBuilder;
use tera::Filter;

use crate::{config::Config, site::Site};

/// This filter takes a markdown string as input and outputs safe HTML.
pub struct Markdownify {
    /// The website's project directory, same as [`crate::site::Site::path`]
    root: PathBuf,

    /// The website's [`Config`]
    config: Config,
}

impl Markdownify {
    pub fn new(root: &Path, config: &Config) -> Self {
        Self {
            root: root.to_owned(),
            config: config.clone(),
        }
    }
}

impl Filter for Markdownify {
    fn filter(
        &self,
        value: &tera::Value,
        _: &std::collections::HashMap<String, tera::Value>,
    ) -> tera::Result<tera::Value> {
        // TODO: this impl is suboptimal and 99% a copy-paste from Site,
        // but the comrak_opts and plugins are not thread safe and trying to
        // declare them once drove me absolutely insane
        if let Some(md) = value.as_str() {
            let mut comrak_opts = comrak::Options::default();
            comrak_opts.extension.front_matter_delimiter = Some("+++".into());
            comrak_opts.extension.table = true;
            comrak_opts.extension.header_ids = if self.config.heading_ids {
                Some(String::default())
            } else {
                None
            };
            let mut comrak_plugins = comrak::Plugins::default();
            let syntax_highlight_adapter =
                if let Some((set, name)) = Site::load_syntect_theme(&self.root, &self.config) {
                    let syn_set = two_face::syntax::extra_newlines();
                    Some(
                        SyntectAdapterBuilder::new()
                            .theme_set(set)
                            .theme(&name)
                            .syntax_set(syn_set)
                            .build(),
                    )
                } else {
                    None
                };
            if let Some(adapter) = syntax_highlight_adapter.as_ref() {
                comrak_plugins.render.codefence_syntax_highlighter = Some(adapter);
            }
            Ok(comrak::markdown_to_html_with_plugins(md, &comrak_opts, &comrak_plugins).into())
        } else {
            Err(tera::Error::msg("markdownify: value is not string"))
        }
    }

    fn is_safe(&self) -> bool {
        true
    }
}
