use std::{fmt, sync::Arc};

use anyhow::Context;
use handlebars::Handlebars;
use tracing::instrument;

use crate::{
    sources::Sources,
    vfs::{Content, Dir, Query, VPath},
};

use super::BaseTemplateData;

pub struct SimpleTemplateDir {
    sources: Arc<Sources>,
    handlebars: Arc<Handlebars<'static>>,
}

impl SimpleTemplateDir {
    pub fn new(sources: Arc<Sources>, handlebars: Arc<Handlebars<'static>>) -> Self {
        Self {
            sources,
            handlebars,
        }
    }

    #[instrument(name = "simple_template::generate", skip(self))]
    fn generate(&self, template_name: &str) -> anyhow::Result<String> {
        let base_template_data = BaseTemplateData::new(&self.sources);
        self.handlebars
            .render(template_name, &base_template_data)
            .context("failed to render template")
    }

    fn generate_or_error(&self, template_name: &str) -> String {
        match self.generate(template_name) {
            Ok(html) => html,
            Err(error) => format!("error: {error:?}"),
        }
    }

    #[instrument("TreehouseDir::content", skip(self))]
    fn content(&self, path: &VPath) -> Option<Content> {
        if path.file_name().is_some_and(|s| !s.starts_with('_')) {
            let template_name = path.with_extension("hbs");
            if self.handlebars.has_template(template_name.as_str()) {
                return Some(Content::new(
                    "text/html",
                    self.generate_or_error(template_name.as_str()).into(),
                ));
            }
        }

        None
    }
}

impl Dir for SimpleTemplateDir {
    fn query(&self, path: &VPath, query: &mut Query) {
        // NOTE: An implementation of Entries is not currently provided, because SimpleTemplateDir
        // isn't used enough to need one.
        query.try_provide(|| self.content(path));
    }
}

impl fmt::Debug for SimpleTemplateDir {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("SimpleTemplateDir")
    }
}
