use std::collections::HashMap;
use std::path::Path;
use crate::{Template, TvaiError, TvaiResult};

/// Template manager for loading, storing and managing templates
#[derive(Debug, Default)]
pub struct TemplateManager {
    /// Loaded templates indexed by name
    templates: HashMap<String, Template>,
    /// Template file paths
    template_paths: HashMap<String, String>,
}

impl TemplateManager {
    /// Create a new template manager
    pub fn new() -> Self {
        Self {
            templates: HashMap::new(),
            template_paths: HashMap::new(),
        }
    }

    /// Load templates from a directory
    pub fn load_from_directory<P: AsRef<Path>>(&mut self, dir_path: P) -> TvaiResult<()> {
        let dir_path = dir_path.as_ref();
        
        if !dir_path.exists() {
            return Err(TvaiError::IoError(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                format!("Directory not found: {}", dir_path.display())
            )));
        }

        let entries = std::fs::read_dir(dir_path)?;
        
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            
            if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("json") {
                match self.load_from_file(&path) {
                    Ok(template) => {
                        self.template_paths.insert(template.name.clone(), path.to_string_lossy().to_string());
                    },
                    Err(e) => {
                        eprintln!("Warning: Failed to load template from {}: {}", path.display(), e);
                    }
                }
            }
        }

        Ok(())
    }

    /// Load a single template from file
    pub fn load_from_file<P: AsRef<Path>>(&mut self, file_path: P) -> TvaiResult<Template> {
        let mut template = Template::from_file(&file_path)?;
        
        // Validate and complete the template
        template.validate_and_complete()?;
        
        // Store the template
        self.templates.insert(template.name.clone(), template.clone());
        
        Ok(template)
    }

    /// Add a template to the manager
    pub fn add_template(&mut self, mut template: Template) -> TvaiResult<()> {
        // Validate and complete the template
        template.validate_and_complete()?;
        
        self.templates.insert(template.name.clone(), template);
        Ok(())
    }

    /// Remove a template by name
    pub fn remove_template(&mut self, name: &str) -> Option<Template> {
        self.template_paths.remove(name);
        self.templates.remove(name)
    }

    /// Get all templates
    pub fn get_templates(&self) -> Vec<&Template> {
        self.templates.values().collect()
    }

    /// Get template names
    pub fn get_template_names(&self) -> Vec<&String> {
        self.templates.keys().collect()
    }

    /// Find template by name
    pub fn find_by_name(&self, name: &str) -> Option<&Template> {
        self.templates.get(name)
    }

    /// Find template by name (mutable)
    pub fn find_by_name_mut(&mut self, name: &str) -> Option<&mut Template> {
        self.templates.get_mut(name)
    }

    /// Check if template exists
    pub fn contains_template(&self, name: &str) -> bool {
        self.templates.contains_key(name)
    }

    /// Get template count
    pub fn template_count(&self) -> usize {
        self.templates.len()
    }

    /// Clear all templates
    pub fn clear(&mut self) {
        self.templates.clear();
        self.template_paths.clear();
    }

    /// Export templates to JSON
    pub fn export_templates_to_json(&self) -> TvaiResult<String> {
        let templates: Vec<&Template> = self.templates.values().collect();
        serde_json::to_string_pretty(&templates).map_err(TvaiError::JsonSerializeError)
    }

    /// Import templates from JSON
    pub fn import_templates_from_json(&mut self, json: &str) -> TvaiResult<usize> {
        let templates: Vec<Template> = serde_json::from_str(json)?;
        let mut count = 0;
        
        for mut template in templates {
            if let Err(e) = template.validate_and_complete() {
                eprintln!("Warning: Failed to validate template '{}': {}", template.name, e);
                continue;
            }
            
            self.templates.insert(template.name.clone(), template);
            count += 1;
        }
        
        Ok(count)
    }

    /// Save all templates to directory
    pub fn save_templates_to_directory<P: AsRef<Path>>(&self, dir_path: P) -> TvaiResult<()> {
        let dir_path = dir_path.as_ref();
        
        // Create directory if it doesn't exist
        if !dir_path.exists() {
            std::fs::create_dir_all(dir_path)?;
        }

        for template in self.templates.values() {
            let filename = format!("{}.json", template.name.replace("/", "_").replace("\\", "_"));
            let file_path = dir_path.join(filename);
            template.save_to_file(file_path)?;
        }

        Ok(())
    }

    /// Get template file path
    pub fn get_template_path(&self, name: &str) -> Option<&String> {
        self.template_paths.get(name)
    }

    /// Validate all templates
    pub fn validate_all_templates(&mut self) -> Vec<(String, TvaiError)> {
        let mut errors = Vec::new();
        
        for (name, template) in &mut self.templates {
            if let Err(e) = template.validate_and_complete() {
                errors.push((name.clone(), e));
            }
        }
        
        errors
    }
}

/// Database interface trait for template storage
pub trait TemplateDatabase {
    /// Save template to database
    fn save_template(&self, template: &Template) -> TvaiResult<()>;
    
    /// Load template from database
    fn load_template(&self, name: &str) -> TvaiResult<Template>;
    
    /// Load all templates from database
    fn load_all_templates(&self) -> TvaiResult<Vec<Template>>;
    
    /// Delete template from database
    fn delete_template(&self, name: &str) -> TvaiResult<()>;
    
    /// Check if template exists in database
    fn template_exists(&self, name: &str) -> TvaiResult<bool>;
}

/// Template manager with database support
pub struct DatabaseTemplateManager<D: TemplateDatabase> {
    manager: TemplateManager,
    database: D,
}

impl<D: TemplateDatabase> DatabaseTemplateManager<D> {
    /// Create new database template manager
    pub fn new(database: D) -> Self {
        Self {
            manager: TemplateManager::new(),
            database,
        }
    }

    /// Load all templates from database
    pub fn load_from_database(&mut self) -> TvaiResult<()> {
        let templates = self.database.load_all_templates()?;
        
        for template in templates {
            self.manager.add_template(template)?;
        }
        
        Ok(())
    }

    /// Save template to database
    pub fn save_template_to_database(&self, template: &Template) -> TvaiResult<()> {
        self.database.save_template(template)
    }

    /// Get the underlying template manager
    pub fn manager(&self) -> &TemplateManager {
        &self.manager
    }

    /// Get the underlying template manager (mutable)
    pub fn manager_mut(&mut self) -> &mut TemplateManager {
        &mut self.manager
    }
}
