//! Template configuration parser for .tpl.toml files

#![allow(dead_code)]

use std::fs;
use std::path::Path;

use crate::template::{Result, TemplateError, TemplatePageConfig};

/// Template configuration manager
#[derive(Debug, Clone)]
pub struct TemplateConfig {
    /// Base directory for templates
    pub template_dir: String,
    /// Base directory for content files
    pub content_dir: String,
    /// Default template extension
    pub template_extension: String,
    /// Cache enabled flag
    pub cache_enabled: bool,
}

#[allow(dead_code)]
impl TemplateConfig {
    /// Create a new template configuration
    pub fn new(template_dir: impl Into<String>, content_dir: impl Into<String>) -> Self {
        Self {
            template_dir: template_dir.into(),
            content_dir: content_dir.into(),
            template_extension: "html".to_string(),
            cache_enabled: true,
        }
    }

    /// Set template extension
    pub fn with_extension(mut self, extension: impl Into<String>) -> Self {
        self.template_extension = extension.into();
        self
    }

    /// Enable or disable cache
    pub fn with_cache(mut self, enabled: bool) -> Self {
        self.cache_enabled = enabled;
        self
    }

    /// Load template page configuration from a .tpl.toml file
    pub fn load_page_config(&self, file_path: &Path) -> Result<TemplatePageConfig> {
        let content = fs::read_to_string(file_path).map_err(|e| TemplateError::IoError(e))?;

        let config: TemplatePageConfig =
            toml::from_str(&content).map_err(|e| TemplateError::TomlError(e))?;

        // Validate that template_name is not empty
        if config.template_name.is_empty() {
            return Err(TemplateError::InvalidConfig(
                "template_name cannot be empty".to_string(),
            ));
        }

        Ok(config)
    }

    /// Get the full path to a template file
    pub fn get_template_path(&self, template_name: &str) -> String {
        let template_file = if template_name.ends_with(&format!(".{}", self.template_extension)) {
            template_name.to_string()
        } else {
            format!("{}.{}", template_name, self.template_extension)
        };

        format!("{}/{}", self.template_dir, template_file)
    }

    /// Check if a template file exists
    pub fn template_exists(&self, template_name: &str) -> bool {
        let template_path = self.get_template_path(template_name);
        Path::new(&template_path).exists()
    }

    /// Load all .tpl.toml files from the content directory
    pub fn load_all_page_configs(&self) -> Result<Vec<(String, TemplatePageConfig)>> {
        let mut configs = Vec::new();
        self.load_configs_from_dir(&self.content_dir, &mut configs)?;
        Ok(configs)
    }

    /// Recursively load .tpl.toml files from a directory
    fn load_configs_from_dir(
        &self,
        dir: &str,
        configs: &mut Vec<(String, TemplatePageConfig)>,
    ) -> Result<()> {
        let dir_path = Path::new(dir);

        if !dir_path.exists() {
            return Ok(());
        }

        for entry in fs::read_dir(dir_path)? {
            let entry = entry?;
            let path = entry.path();

            if path.is_dir() {
                // Recursively process subdirectories
                if let Some(path_str) = path.to_str() {
                    self.load_configs_from_dir(path_str, configs)?;
                }
            } else if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
                if file_name.ends_with(".tpl.toml") {
                    match self.load_page_config(&path) {
                        Ok(config) => {
                            let file_path = path.to_string_lossy().to_string();
                            configs.push((file_path, config));
                        }
                        Err(e) => {
                            eprintln!(
                                "Warning: Failed to load template config from {}: {}",
                                path.display(),
                                e
                            );
                        }
                    }
                }
            }
        }

        Ok(())
    }

    /// Extract the base name from a .tpl.toml file path
    pub fn extract_base_name(&self, file_path: &str) -> String {
        Path::new(file_path)
            .file_stem()
            .and_then(|stem| stem.to_str())
            .unwrap_or("unknown")
            .replace(".tpl", "")
    }

    /// Generate a slug from a file path
    pub fn generate_slug(&self, file_path: &str) -> String {
        let base_name = self.extract_base_name(file_path);

        // Convert to lowercase and replace spaces/underscores with hyphens
        base_name
            .to_lowercase()
            .replace(|c: char| !c.is_alphanumeric() && c != '-', "-")
            .trim_matches('-')
            .to_string()
    }
}

impl Default for TemplateConfig {
    fn default() -> Self {
        let config = rustelo_core_lib::config::ContentConfig::from_env();
        Self::new(
            config.templates_path.to_string_lossy().as_ref(),
            config.root_path.join("docs").to_string_lossy().as_ref(),
        )
    }
}

#[cfg(test)]
mod tests {
    use std::fs;

    use tempfile::TempDir;

    use super::*;

    fn create_test_config_file(dir: &Path, name: &str, content: &str) -> std::io::Result<()> {
        let file_path = dir.join(name);
        fs::write(file_path, content)
    }

    #[test]
    fn test_template_config_creation() {
        let config = TemplateConfig::new("templates", "content");
        assert_eq!(config.template_dir, "templates");
        assert_eq!(config.content_dir, "content");
        assert_eq!(config.template_extension, "html");
        assert!(config.cache_enabled);
    }

    #[test]
    fn test_template_path_generation() {
        let config = TemplateConfig::new("templates", "content");

        assert_eq!(config.get_template_path("page"), "templates/page.html");
        assert_eq!(config.get_template_path("page.html"), "templates/page.html");
    }

    #[test]
    fn test_load_page_config() {
        let temp_dir = TempDir::new().unwrap();
        let config_content = r#"
template_name = "blog-post"

[values]
title = "My Blog Post"
author = "John Doe"
published = true
tags = ["rust", "web"]
"#;

        create_test_config_file(temp_dir.path(), "test.tpl.toml", config_content).unwrap();

        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let page_config = config
            .load_page_config(&temp_dir.path().join("test.tpl.toml"))
            .unwrap();

        assert_eq!(page_config.template_name, "blog-post");
        assert_eq!(page_config.values.get("title").unwrap(), "My Blog Post");
        assert_eq!(page_config.values.get("author").unwrap(), "John Doe");
        assert_eq!(page_config.values.get("published").unwrap(), true);
    }

    #[test]
    fn test_extract_base_name() {
        let config = TemplateConfig::default();

        assert_eq!(config.extract_base_name("test.tpl.toml"), "test");
        assert_eq!(config.extract_base_name("path/to/file.tpl.toml"), "file");
        assert_eq!(
            config.extract_base_name("complex-name.tpl.toml"),
            "complex-name"
        );
    }

    #[test]
    fn test_generate_slug() {
        let config = TemplateConfig::default();

        assert_eq!(config.generate_slug("test.tpl.toml"), "test");
        assert_eq!(
            config.generate_slug("My Blog Post.tpl.toml"),
            "my-blog-post"
        );
        assert_eq!(
            config.generate_slug("complex_file_name.tpl.toml"),
            "complex-file-name"
        );
    }

    #[test]
    fn test_invalid_config() {
        let temp_dir = TempDir::new().unwrap();
        let invalid_config = r#"
# Missing template_name
[values]
title = "Test"
"#;

        create_test_config_file(temp_dir.path(), "invalid.tpl.toml", invalid_config).unwrap();

        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let result = config.load_page_config(&temp_dir.path().join("invalid.tpl.toml"));

        assert!(result.is_err());
    }
}
