//! Template loader for localized .tpl.toml files

#![allow(dead_code)]

use std::collections::HashMap;
use std::fs;
use std::path::Path;

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

/// Template loader with localization support
#[derive(Debug, Clone)]
pub struct TemplateLoader {
    /// Template configuration
    config: TemplateConfig,
    /// Default language
    default_lang: String,
    /// Available languages
    available_langs: Vec<String>,
    /// Cache for loaded templates
    cache: HashMap<String, TemplatePageConfig>,
    /// Cache enabled flag
    cache_enabled: bool,
}

#[allow(dead_code)]
impl TemplateLoader {
    /// Create a new template loader
    pub fn new(config: TemplateConfig) -> Self {
        Self {
            config,
            default_lang: "en".to_string(),
            available_langs: vec!["en".to_string()],
            cache: HashMap::new(),
            cache_enabled: true,
        }
    }

    /// Set the default language
    pub fn with_default_lang(mut self, lang: impl Into<String>) -> Self {
        self.default_lang = lang.into();
        self
    }

    /// Set available languages
    pub fn with_languages(mut self, langs: Vec<String>) -> Self {
        self.available_langs = langs;
        self
    }

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

    /// Load a localized template page configuration
    ///
    /// For URL `/page:content-name` and language `en`, this will look for:
    /// `content-dir/en_content-name.tpl.toml`
    pub fn load_page_config(
        &mut self,
        content_name: &str,
        lang: &str,
    ) -> Result<TemplatePageConfig> {
        let cache_key = format!("{}_{}", lang, content_name);

        // Check cache first
        if self.cache_enabled {
            if let Some(cached) = self.cache.get(&cache_key) {
                return Ok(cached.clone());
            }
        }

        // Generate the localized filename
        let filename = format!("{}_{}.tpl.toml", lang, content_name);
        let file_path = Path::new(&self.config.content_dir).join(&filename);

        // Try to load the localized file
        let config = if file_path.exists() {
            self.config.load_page_config(&file_path)?
        } else {
            // Fallback to default language if the requested language doesn't exist
            if lang != self.default_lang {
                let default_filename = format!("{}_{}.tpl.toml", self.default_lang, content_name);
                let default_file_path = Path::new(&self.config.content_dir).join(&default_filename);

                if default_file_path.exists() {
                    self.config.load_page_config(&default_file_path)?
                } else {
                    return Err(TemplateError::ConfigNotFound(format!(
                        "Template config not found for '{}' in language '{}' or default language \
                         '{}'",
                        content_name, lang, self.default_lang
                    )));
                }
            } else {
                return Err(TemplateError::ConfigNotFound(format!(
                    "Template config not found: {}",
                    file_path.display()
                )));
            }
        };

        // Cache the result
        if self.cache_enabled {
            self.cache.insert(cache_key, config.clone());
        }

        Ok(config)
    }

    /// Load a template config with fallback chain
    ///
    /// 1. Try requested language: `lang_content-name.tpl.toml`
    /// 2. Try default language: `default_lang_content-name.tpl.toml`
    /// 3. Try without language prefix: `content-name.tpl.toml`
    pub fn load_page_config_with_fallback(
        &mut self,
        content_name: &str,
        lang: &str,
    ) -> Result<TemplatePageConfig> {
        // First try the requested language
        let primary_filename = format!("{}_{}.tpl.toml", lang, content_name);
        let primary_path = Path::new(&self.config.content_dir).join(&primary_filename);

        if primary_path.exists() {
            let config = self.config.load_page_config(&primary_path)?;
            if self.cache_enabled {
                self.cache
                    .insert(format!("{}_{}", lang, content_name), config.clone());
            }
            return Ok(config);
        }

        // Try default language if different from requested
        if lang != self.default_lang {
            let default_filename = format!("{}_{}.tpl.toml", self.default_lang, content_name);
            let default_path = Path::new(&self.config.content_dir).join(&default_filename);

            if default_path.exists() {
                let config = self.config.load_page_config(&default_path)?;
                if self.cache_enabled {
                    self.cache
                        .insert(format!("{}_{}", lang, content_name), config.clone());
                }
                return Ok(config);
            }
        }

        // Try without language prefix as final fallback
        let fallback_filename = format!("{}.tpl.toml", content_name);
        let fallback_path = Path::new(&self.config.content_dir).join(&fallback_filename);

        if fallback_path.exists() {
            let config = self.config.load_page_config(&fallback_path)?;
            if self.cache_enabled {
                self.cache
                    .insert(format!("{}_{}", lang, content_name), config.clone());
            }
            return Ok(config);
        }

        Err(TemplateError::ConfigNotFound(format!(
            "Template config not found for '{}' in any language (tried: {}, {}, no-prefix)",
            content_name, lang, self.default_lang
        )))
    }

    /// Load all available template configurations for a specific language
    pub fn load_all_for_language(
        &mut self,
        lang: &str,
    ) -> Result<Vec<(String, TemplatePageConfig)>> {
        let mut configs = Vec::new();
        let content_dir = Path::new(&self.config.content_dir);

        if !content_dir.exists() {
            return Ok(configs);
        }

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

            if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
                if filename.ends_with(".tpl.toml") {
                    // Check if this file matches our language pattern
                    let lang_prefix = format!("{}_", lang);
                    if filename.starts_with(&lang_prefix) {
                        // Extract content name by removing language prefix and .tpl.toml suffix
                        let content_name = filename
                            .strip_prefix(&lang_prefix)
                            .and_then(|s| s.strip_suffix(".tpl.toml"))
                            .unwrap_or(filename);

                        match self.config.load_page_config(&path) {
                            Ok(config) => {
                                configs.push((content_name.to_string(), config));
                            }
                            Err(e) => {
                                eprintln!(
                                    "Warning: Failed to load template config from {}: {}",
                                    path.display(),
                                    e
                                );
                            }
                        }
                    }
                }
            }
        }

        Ok(configs)
    }

    /// Get all available content names for a specific language
    pub fn get_available_content(&self, lang: &str) -> Result<Vec<String>> {
        let mut content_names = Vec::new();
        let content_dir = Path::new(&self.config.content_dir);

        if !content_dir.exists() {
            return Ok(content_names);
        }

        let lang_prefix = format!("{}_", lang);

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

            if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
                if filename.ends_with(".tpl.toml") && filename.starts_with(&lang_prefix) {
                    // Extract content name
                    if let Some(content_name) = filename
                        .strip_prefix(&lang_prefix)
                        .and_then(|s| s.strip_suffix(".tpl.toml"))
                    {
                        content_names.push(content_name.to_string());
                    }
                }
            }
        }

        Ok(content_names)
    }

    /// Check if a localized template exists
    pub fn exists(&self, content_name: &str, lang: &str) -> bool {
        let filename = format!("{}_{}.tpl.toml", lang, content_name);
        let file_path = Path::new(&self.config.content_dir).join(&filename);
        file_path.exists()
    }

    /// Parse content name and language from URL path
    ///
    /// For URL `/page:content-name`, extracts `content-name`
    pub fn parse_page_url(&self, url_path: &str) -> Option<String> {
        if url_path.starts_with("/page:") {
            Some(url_path.strip_prefix("/page:")?.to_string())
        } else {
            None
        }
    }

    /// Generate URL path from content name
    ///
    /// Converts `content-name` to `/page:content-name`
    pub fn generate_page_url(&self, content_name: &str) -> String {
        format!("/page:{}", content_name)
    }

    /// Clear the cache
    pub fn clear_cache(&mut self) {
        self.cache.clear();
    }

    /// Get cache statistics
    pub fn get_cache_stats(&self) -> (usize, bool) {
        (self.cache.len(), self.cache_enabled)
    }

    /// Get available languages
    pub fn get_available_languages(&self) -> &[String] {
        &self.available_langs
    }

    /// Get default language
    pub fn get_default_language(&self) -> &str {
        &self.default_lang
    }

    /// Reload a specific template from disk
    pub fn reload_template(&mut self, content_name: &str, lang: &str) -> Result<()> {
        let cache_key = format!("{}_{}", lang, content_name);
        self.cache.remove(&cache_key);

        // Force reload by calling load_page_config
        self.load_page_config(content_name, lang)?;
        Ok(())
    }

    /// Get template source file path
    pub fn get_template_file_path(&self, content_name: &str, lang: &str) -> String {
        let filename = format!("{}_{}.tpl.toml", lang, content_name);
        Path::new(&self.config.content_dir)
            .join(&filename)
            .to_string_lossy()
            .to_string()
    }
}

impl Default for TemplateLoader {
    fn default() -> Self {
        Self::new(TemplateConfig::default())
    }
}

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

    use tempfile::TempDir;

    use super::*;

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

    #[test]
    fn test_template_loader_creation() {
        let temp_dir = TempDir::new().unwrap();
        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let loader = TemplateLoader::new(config);

        assert_eq!(loader.default_lang, "en");
        assert_eq!(loader.available_langs, vec!["en"]);
    }

    #[test]
    fn test_localized_template_loading() {
        let temp_dir = TempDir::new().unwrap();
        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let mut loader = TemplateLoader::new(config);

        // Create test template files
        let en_content = r#"
template_name = "content-post"

[values]
title = "English Title"
content = "English content"
"#;

        let es_content = r#"
template_name = "content-post"

[values]
title = "Título en Español"
content = "Contenido en español"
"#;

        create_test_template_file(temp_dir.path(), "en_my-content.tpl.toml", en_content).unwrap();
        create_test_template_file(temp_dir.path(), "es_my-content.tpl.toml", es_content).unwrap();

        // Test loading English version
        let en_config = loader.load_page_config("my-content", "en").unwrap();
        assert_eq!(en_config.values.get("title").unwrap(), "English Title");

        // Test loading Spanish version
        let es_config = loader.load_page_config("my-content", "es").unwrap();
        assert_eq!(es_config.values.get("title").unwrap(), "Título en Español");
    }

    #[test]
    fn test_fallback_to_default_language() {
        let temp_dir = TempDir::new().unwrap();
        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let mut loader = TemplateLoader::new(config);

        // Create only English version
        let en_content = r#"
template_name = "content-post"

[values]
title = "English Title"
"#;

        create_test_template_file(temp_dir.path(), "en_my-content.tpl.toml", en_content).unwrap();

        // Try to load unsupported language version, should fallback to English
        let config = loader.load_page_config("my-content", "de").unwrap();
        assert_eq!(config.values.get("title").unwrap(), "English Title");
    }

    #[test]
    fn test_parse_page_url() {
        let loader = TemplateLoader::default();

        assert_eq!(
            loader.parse_page_url("/page:my-content"),
            Some("my-content".to_string())
        );
        assert_eq!(
            loader.parse_page_url("/page:about-us"),
            Some("about-us".to_string())
        );
        assert_eq!(loader.parse_page_url("/other-path"), None);
    }

    #[test]
    fn test_generate_page_url() {
        let loader = TemplateLoader::default();

        assert_eq!(loader.generate_page_url("my-content"), "/page:my-content");
        assert_eq!(loader.generate_page_url("about-us"), "/page:about-us");
    }

    #[test]
    fn test_get_available_content() {
        let temp_dir = TempDir::new().unwrap();
        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let loader = TemplateLoader::new(config);

        // Create test files
        create_test_template_file(
            temp_dir.path(),
            "en_content-post.tpl.toml",
            "template_name = \"test\"",
        )
        .unwrap();
        create_test_template_file(
            temp_dir.path(),
            "en_about.tpl.toml",
            "template_name = \"test\"",
        )
        .unwrap();
        create_test_template_file(
            temp_dir.path(),
            "es_content-post.tpl.toml",
            "template_name = \"test\"",
        )
        .unwrap();

        let en_content = loader.get_available_content("en").unwrap();
        assert_eq!(en_content.len(), 2);
        assert!(en_content.contains(&"content-post".to_string()));
        assert!(en_content.contains(&"about".to_string()));

        let es_content = loader.get_available_content("es").unwrap();
        assert_eq!(es_content.len(), 1);
        assert!(es_content.contains(&"content-post".to_string()));
    }

    #[test]
    fn test_template_exists() {
        let temp_dir = TempDir::new().unwrap();
        let config = TemplateConfig::new("templates", temp_dir.path().to_str().unwrap());
        let loader = TemplateLoader::new(config);

        create_test_template_file(
            temp_dir.path(),
            "en_existing.tpl.toml",
            "template_name = \"test\"",
        )
        .unwrap();

        assert!(loader.exists("existing", "en"));
        assert!(!loader.exists("non-existing", "en"));
        assert!(!loader.exists("existing", "fr"));
    }
}
