//! Plugin and resource registration system
//!
//! This module provides a framework-level registration system that allows:
//! - Implementation-generated code to register resources dynamically
//! - Config-driven resource loading from TOML files
//! - Multiple contributors (implementation, plugins) to coexist
//! - No hardcoding or conditional compilation in framework
//!
//! The key insight: Framework provides EMPTY registries + registration API.
//! Implementation/plugins include THEIR OWN generated code and register it.

use once_cell::sync::Lazy;
use serde::Deserialize;
use std::collections::HashMap;
use std::sync::RwLock;

// ============================================================================
// REGISTRATION TRAITS
// ============================================================================

/// Error type for registration operations
#[derive(Debug, Clone)]
pub enum RegistrationError {
    /// Resource already registered
    DuplicateResource(String),
    /// Required resource not found
    NotFound(String),
    /// Configuration error
    ConfigError(String),
    /// IO error
    IoError(String),
}

impl std::fmt::Display for RegistrationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::DuplicateResource(key) => write!(f, "Resource already registered: {}", key),
            Self::NotFound(key) => write!(f, "Resource not found: {}", key),
            Self::ConfigError(msg) => write!(f, "Configuration error: {}", msg),
            Self::IoError(msg) => write!(f, "IO error: {}", msg),
        }
    }
}

impl std::error::Error for RegistrationError {}

/// Trait for types that contribute resources to the system
///
/// Implementations include: framework defaults, generated code from build.rs, plugins
pub trait ResourceContributor: Send + Sync {
    /// Contribute menu resources
    /// Returns: HashMap<language, TOML content>
    fn contribute_menus(&self) -> HashMap<String, String> {
        HashMap::new()
    }

    /// Contribute footer resources
    /// Returns: HashMap<name, TOML content>
    fn contribute_footers(&self) -> HashMap<String, String> {
        HashMap::new()
    }

    /// Contribute theme resources
    /// Returns: HashMap<theme_name, TOML content>
    fn contribute_themes(&self) -> HashMap<String, String> {
        HashMap::new()
    }

    /// Contribute FTL (Fluent) translation resources
    /// Returns: HashMap<language, FTL content>
    fn contribute_ftl(&self) -> HashMap<String, String> {
        HashMap::new()
    }

    /// Contributor name (for debugging)
    fn name(&self) -> &str {
        "unknown"
    }
}

/// Trait for types that contribute pages/components
pub trait PageContributor: Send + Sync {
    /// List of page component names this contributor provides
    fn provided_pages(&self) -> Vec<String>;

    /// Contributor name (for debugging)
    fn name(&self) -> &str {
        "unknown"
    }
}

// ============================================================================
// RESOURCE REGISTRIES (EMPTY - to be populated by contributors)
// ============================================================================

/// Global menu registry: language -> TOML content string
pub static MENU_REGISTRY: Lazy<RwLock<HashMap<String, String>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Global footer registry: name -> TOML content string
pub static FOOTER_REGISTRY: Lazy<RwLock<HashMap<String, String>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Global theme registry: theme_name -> TOML content string
pub static THEME_REGISTRY: Lazy<RwLock<HashMap<String, String>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Global FTL (translation) registry: language -> FTL content string
pub static FTL_REGISTRY: Lazy<RwLock<HashMap<String, String>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Global page registry: component_name -> page definition
pub static PAGE_REGISTRY: Lazy<RwLock<HashMap<String, String>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

// ============================================================================
// REGISTRATION API
// ============================================================================

/// Register a menu resource
pub fn register_menu(language: String, content: String) -> Result<(), RegistrationError> {
    let mut registry = MENU_REGISTRY
        .write()
        .map_err(|e| RegistrationError::ConfigError(format!("Failed to acquire lock: {}", e)))?;

    if registry.contains_key(&language) {
        return Err(RegistrationError::DuplicateResource(format!(
            "Menu for language: {}",
            language
        )));
    }

    registry.insert(language, content);
    Ok(())
}

/// Register a footer resource
pub fn register_footer(name: String, content: String) -> Result<(), RegistrationError> {
    let mut registry = FOOTER_REGISTRY
        .write()
        .map_err(|e| RegistrationError::ConfigError(format!("Failed to acquire lock: {}", e)))?;

    if registry.contains_key(&name) {
        return Err(RegistrationError::DuplicateResource(format!(
            "Footer: {}",
            name
        )));
    }

    registry.insert(name, content);
    Ok(())
}

/// Register a theme resource
pub fn register_theme(name: String, content: String) -> Result<(), RegistrationError> {
    let mut registry = THEME_REGISTRY
        .write()
        .map_err(|e| RegistrationError::ConfigError(format!("Failed to acquire lock: {}", e)))?;

    if registry.contains_key(&name) {
        return Err(RegistrationError::DuplicateResource(format!(
            "Theme: {}",
            name
        )));
    }

    registry.insert(name, content);
    Ok(())
}

/// Register a FTL (translation) resource
pub fn register_ftl(language: String, content: String) -> Result<(), RegistrationError> {
    let mut registry = FTL_REGISTRY
        .write()
        .map_err(|e| RegistrationError::ConfigError(format!("Failed to acquire lock: {}", e)))?;

    if registry.contains_key(&language) {
        return Err(RegistrationError::DuplicateResource(format!(
            "FTL for language: {}",
            language
        )));
    }

    registry.insert(language, content);
    Ok(())
}

/// Register all resources from a contributor
pub fn register_contributor(
    contributor: &dyn ResourceContributor,
) -> Result<(), RegistrationError> {
    // Register menus
    for (language, content) in contributor.contribute_menus() {
        register_menu(language, content)?;
    }

    // Register footers
    for (name, content) in contributor.contribute_footers() {
        register_footer(name, content)?;
    }

    // Register themes
    for (name, content) in contributor.contribute_themes() {
        register_theme(name, content)?;
    }

    // Register FTL
    for (language, content) in contributor.contribute_ftl() {
        register_ftl(language, content)?;
    }

    Ok(())
}

// ============================================================================
// RESOURCE ACCESS API (reads from registries)
// ============================================================================

/// Get a menu resource by language
pub fn get_menu(language: &str) -> Option<String> {
    MENU_REGISTRY.read().ok()?.get(language).cloned()
}

/// Get a footer resource by name
pub fn get_footer(name: &str) -> Option<String> {
    FOOTER_REGISTRY.read().ok()?.get(name).cloned()
}

/// Get a theme resource by name
pub fn get_theme(name: &str) -> Option<String> {
    THEME_REGISTRY.read().ok()?.get(name).cloned()
}

/// Get a FTL (translation) resource by language
pub fn get_ftl(language: &str) -> Option<String> {
    FTL_REGISTRY.read().ok()?.get(language).cloned()
}

/// List all registered languages (menus)
pub fn list_languages() -> Vec<String> {
    MENU_REGISTRY
        .read()
        .map(|r| r.keys().cloned().collect())
        .unwrap_or_default()
}

/// List all registered themes
pub fn list_themes() -> Vec<String> {
    THEME_REGISTRY
        .read()
        .map(|r| r.keys().cloned().collect())
        .unwrap_or_default()
}

/// List all registered pages
pub fn list_pages() -> Vec<String> {
    PAGE_REGISTRY
        .read()
        .map(|r| r.keys().cloned().collect())
        .unwrap_or_default()
}

// ============================================================================
// CONFIG-DRIVEN LOADER (PAP-COMPLIANT)
// ============================================================================

/// Load resources from configuration paths (PAP-compliant)
///
/// This function loads resources from TOML files at runtime, respecting
/// environment variables and the manifest system.
pub fn load_resources_from_config() -> Result<(), RegistrationError> {
    // Get config root from path resolution module (respects SITE_CONFIG_PATH env var)
    let config_root = std::path::PathBuf::from(crate::path_resolution::get_config_path());

    // Load menus from config/menus/*.toml (required)
    load_menus_from_config(&config_root)?;

    // Load footers from config/footers/*.toml (optional)
    // Errors in optional folders are logged but not returned
    let _ = load_footers_from_config(&config_root);

    // Load themes from config/themes/*.toml (optional)
    let _ = load_themes_from_config(&config_root);

    // Load FTL translations from content/i18n/*.ftl (optional)
    let _ = load_ftl_from_config(&config_root);

    Ok(())
}

/// Load menu configurations from config/menus/*.toml
fn load_menus_from_config(config_root: &std::path::Path) -> Result<(), RegistrationError> {
    let menus_dir = config_root.join("menus");

    if !menus_dir.exists() {
        return Ok(()); // Directory doesn't exist is not an error
    }

    for entry in std::fs::read_dir(&menus_dir)
        .map_err(|e| RegistrationError::IoError(format!("Failed to read menus directory: {}", e)))?
    {
        let entry = entry.map_err(|e| {
            RegistrationError::IoError(format!("Failed to read directory entry: {}", e))
        })?;

        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) == Some("toml") {
            if let Some(language) = path.file_stem().and_then(|s| s.to_str()) {
                let content = std::fs::read_to_string(&path).map_err(|e| {
                    RegistrationError::IoError(format!(
                        "Failed to read menu file {:?}: {}",
                        path, e
                    ))
                })?;

                register_menu(language.to_string(), content)?;
            }
        }
    }

    Ok(())
}

/// Load footer configurations from config/footers/*.toml (optional)
fn load_footers_from_config(config_root: &std::path::Path) -> Result<(), RegistrationError> {
    let footers_dir = config_root.join("footers");

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

    for entry in std::fs::read_dir(&footers_dir).map_err(|e| {
        RegistrationError::IoError(format!("Failed to read footers directory: {}", e))
    })? {
        let entry = entry.map_err(|e| {
            RegistrationError::IoError(format!("Failed to read directory entry: {}", e))
        })?;

        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) == Some("toml") {
            if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
                let content = std::fs::read_to_string(&path).map_err(|e| {
                    RegistrationError::IoError(format!(
                        "Failed to read footer file {:?}: {}",
                        path, e
                    ))
                })?;

                register_footer(name.to_string(), content)?;
            }
        }
    }

    Ok(())
}

/// Load theme configurations from config/themes/*.toml (optional)
fn load_themes_from_config(config_root: &std::path::Path) -> Result<(), RegistrationError> {
    let themes_dir = config_root.join("themes");

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

    for entry in std::fs::read_dir(&themes_dir).map_err(|e| {
        RegistrationError::IoError(format!("Failed to read themes directory: {}", e))
    })? {
        let entry = entry.map_err(|e| {
            RegistrationError::IoError(format!("Failed to read directory entry: {}", e))
        })?;

        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) == Some("toml") {
            if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
                let content = std::fs::read_to_string(&path).map_err(|e| {
                    RegistrationError::IoError(format!(
                        "Failed to read theme file {:?}: {}",
                        path, e
                    ))
                })?;

                register_theme(name.to_string(), content)?;
            }
        }
    }

    Ok(())
}

/// Load FTL (Fluent) translations from content/i18n/*.ftl or SITE_CONTENT_PATH/i18n/*.ftl
fn load_ftl_from_config(_config_root: &std::path::Path) -> Result<(), RegistrationError> {
    // Get content path from path resolution module (respects SITE_CONTENT_PATH env var)
    let content_root = std::path::PathBuf::from(crate::path_resolution::get_content_path());
    let ftl_root = content_root.join("i18n");

    if !ftl_root.exists() {
        return Ok(()); // Directory doesn't exist is not an error
    }

    for entry in std::fs::read_dir(&ftl_root)
        .map_err(|e| RegistrationError::IoError(format!("Failed to read FTL directory: {}", e)))?
    {
        let entry = entry.map_err(|e| {
            RegistrationError::IoError(format!("Failed to read directory entry: {}", e))
        })?;

        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) == Some("ftl") {
            if let Some(language) = path.file_stem().and_then(|s| s.to_str()) {
                let content = std::fs::read_to_string(&path).map_err(|e| {
                    RegistrationError::IoError(format!("Failed to read FTL file {:?}: {}", path, e))
                })?;

                register_ftl(language.to_string(), content)?;
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_menu_registration() {
        let menu = r#"
[[menu]]
name = "Home"
path = "/"
"#;
        assert!(register_menu("en".to_string(), menu.to_string()).is_ok());
        assert!(get_menu("en").is_some());
    }

    #[test]
    fn test_duplicate_registration_fails() {
        let menu = "[[menu]]\nname = \"Home\"\npath = \"/\"";
        let _ = register_menu("test".to_string(), menu.to_string());
        // Second registration should fail
        let result = register_menu("test".to_string(), menu.to_string());
        assert!(result.is_err());
    }
}
