//! Template engine support using Tera

mod context;
mod filters;

pub use context::TemplateContextBuilder;

use crate::error::{ErrorWrapper, Result};
use std::path::Path;
use tera::Tera;

/// Template engine wrapper around Tera
pub struct TemplateEngine {
    tera: Tera,
}

impl TemplateEngine {
    /// Create a new template engine
    ///
    /// If `templates_path` is provided, loads templates from that directory.
    /// Otherwise creates an engine for inline templates only.
    pub fn new(templates_path: Option<&Path>) -> Result<Self> {
        let mut tera = if let Some(path) = templates_path {
            let pattern = path.join("**/*.tera");
            let pattern_str = pattern.to_str().unwrap_or("templates/**/*.tera");

            Tera::new(pattern_str).map_err(|e| {
                ErrorWrapper::template_failed(format!("Failed to initialize Tera: {}", e))
            })?
        } else {
            Tera::default()
        };

        // Register custom filters
        filters::register_filters(&mut tera);

        Ok(Self { tera })
    }

    /// Add a template string for inline template rendering
    pub fn add_template(&mut self, name: &str, content: &str) -> Result<()> {
        self.tera.add_raw_template(name, content).map_err(|e| {
            ErrorWrapper::template_failed(format!("Failed to add template '{}': {}", name, e))
        })
    }

    /// Render a template by name with context
    pub fn render(&self, template_name: &str, context: &tera::Context) -> Result<String> {
        self.tera.render(template_name, context).map_err(|e| {
            ErrorWrapper::template_failed(format!(
                "Failed to render template '{}': {}",
                template_name, e
            ))
        })
    }

    /// Render a template string directly
    pub fn render_str(&self, template: &str, context: &tera::Context) -> Result<String> {
        Tera::one_off(template, context, false)
            .map_err(|e| ErrorWrapper::template_failed(format!("Failed to render template: {}", e)))
    }

    /// Check if a template exists
    pub fn has_template(&self, name: &str) -> bool {
        self.tera.get_template(name).is_ok()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;
    use std::collections::HashMap;

    #[test]
    fn test_template_engine_new() {
        let engine = TemplateEngine::new(None);
        assert!(engine.is_ok());
    }

    #[test]
    fn test_template_engine_render_str() {
        let engine = TemplateEngine::new(None).unwrap();
        let context = TemplateContextBuilder::new()
            .with_data("name", &"Alice")
            .build();

        let result = engine.render_str("Hello {{ name }}!", &context);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), "Hello Alice!");
    }

    #[test]
    fn test_template_engine_add_and_render() {
        let mut engine = TemplateEngine::new(None).unwrap();
        let result = engine.add_template("greeting", "Hello {{ name }}!");

        assert!(result.is_ok());
        assert!(engine.has_template("greeting"));
    }

    #[test]
    fn test_template_with_results() {
        let engine = TemplateEngine::new(None).unwrap();

        let mut results = HashMap::new();
        results.insert("username".to_string(), json!("bob"));
        results.insert("email".to_string(), json!("bob@example.com"));

        let context = TemplateContextBuilder::new().with_results(&results).build();

        let template = "User: {{ username }}, Email: {{ email }}";
        let result = engine.render_str(template, &context);

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), "User: bob, Email: bob@example.com");
    }

    #[test]
    fn test_template_with_filters() {
        let engine = TemplateEngine::new(None).unwrap();
        let context = TemplateContextBuilder::new()
            .with_data("text", &"hello")
            .build();

        let result = engine.render_str("{{ text | upper }}", &context);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), "HELLO");
    }
}
