//! Configuration management for Rustelo

use std::collections::HashMap;
use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

/// Legal and contact information configuration
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LegalConfig {
    #[serde(default = "default_owner")]
    pub owner: String,
    #[serde(default = "default_activity")]
    pub activity: String,
    #[serde(default = "default_location")]
    pub location: String,
    #[serde(default = "default_country")]
    pub country: String,
    #[serde(default = "default_legal_email")]
    pub legal_email: String,
    #[serde(default = "default_privacy_email")]
    pub privacy_email: String,
    #[serde(default = "default_contact_email")]
    pub contact_email: String,
    #[serde(default = "default_address")]
    pub address: String,
}

/// Logo configuration
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LogoConfig {
    #[serde(default = "default_logo_light")]
    pub light_path: String,
    #[serde(default = "default_logo_dark")]
    pub dark_path: String,
    #[serde(default = "default_logo_icon")]
    pub icon_path: String,
}

/// Application configuration structure
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct AppConfig {
    pub content: ContentManageConfig,
    pub features: HashMap<String, bool>,
    pub legal: LegalConfig,
    pub logo: LogoConfig,
}

/// Content management configuration
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ContentManageConfig {
    #[serde(default = "default_content_root")]
    pub root_path: PathBuf,
    #[serde(default = "default_locales_path")]
    pub locales_path: PathBuf,
    #[serde(default = "default_ftl_path")]
    pub ftl_path: PathBuf,
    #[serde(default = "default_emergency_path")]
    pub emergency_path: PathBuf,
    #[serde(default = "default_themes_path")]
    pub themes_path: PathBuf,
    #[serde(default = "default_menus_path")]
    pub menus_path: PathBuf,
    #[serde(default = "default_footer_path")]
    pub footer_path: PathBuf,
    #[serde(default = "default_templates_path")]
    pub templates_path: PathBuf,
    #[serde(default = "default_public_path")]
    pub public_path: PathBuf,
}

impl Default for ContentManageConfig {
    fn default() -> Self {
        Self {
            root_path: default_content_root(),
            locales_path: default_locales_path(),
            ftl_path: default_ftl_path(),
            emergency_path: default_emergency_path(),
            themes_path: default_themes_path(),
            menus_path: default_menus_path(),
            footer_path: default_footer_path(),
            templates_path: default_templates_path(),
            public_path: default_public_path(),
        }
    }
}

impl Default for LegalConfig {
    fn default() -> Self {
        Self {
            owner: default_owner(),
            activity: default_activity(),
            location: default_location(),
            country: default_country(),
            legal_email: default_legal_email(),
            privacy_email: default_privacy_email(),
            contact_email: default_contact_email(),
            address: default_address(),
        }
    }
}

impl Default for LogoConfig {
    fn default() -> Self {
        Self {
            light_path: default_logo_light(),
            dark_path: default_logo_dark(),
            icon_path: default_logo_icon(),
        }
    }
}

impl AppConfig {
    /// Create AppConfig from environment variables
    pub fn from_env() -> Self {
        Self {
            content: ContentManageConfig::from_env(),
            features: HashMap::new(),
            legal: LegalConfig::from_env(),
            logo: LogoConfig::from_env(),
        }
    }
}

fn default_content_root() -> PathBuf {
    // For server-side content loading, check if we should use the build output
    // directory This is where the generated index.json files are located
    if let Ok(server_content_path) = std::env::var("SITE_SERVER_CONTENT_ROOT") {
        PathBuf::from(server_content_path)
    } else if cfg!(feature = "ssr") {
        // In SSR mode, use the build output directory for generated content
        let public_path = env_or_default("SITE_PUBLIC_PATH", "public");
        let server_root_content = env_or_default("SITE_SERVER_ROOT_CONTENT", "r");
        PathBuf::from(format!("{}/{}", public_path, server_root_content))
    } else {
        // For client builds, use the original content path
        PathBuf::from(env_or_default("SITE_CONTENT_PATH", "content"))
    }
}

fn default_locales_path() -> PathBuf {
    PathBuf::from(env_or_default("SITE_LOCALES_PATH", "site/i18n/locales"))
}

fn default_ftl_path() -> PathBuf {
    PathBuf::from(env_or_default("SITE_FTL_PATH", "site/i18n/locales"))
}

fn default_emergency_path() -> PathBuf {
    PathBuf::from(env_or_default("SITE_EMERGENCY_PATH", "public/tmp"))
}

fn default_themes_path() -> PathBuf {
    let root = env_or_default("SITE_ROOT_PATH", "site");
    PathBuf::from(env_or_default(
        "SITE_THEMES_PATH",
        &format!("{root}/config/themes"),
    ))
}

fn default_menus_path() -> PathBuf {
    let root = env_or_default("SITE_ROOT_PATH", "");
    PathBuf::from(env_or_default(
        "SITE_MENUS_PATH",
        &format!("{root}/ui/menus"),
    ))
}

fn default_footer_path() -> PathBuf {
    let root = env_or_default("SITE_ROOT_PATH", "site");
    PathBuf::from(env_or_default(
        "SITE_FOOTER_PATH",
        &format!("{root}/ui/footer"),
    ))
}

fn default_templates_path() -> PathBuf {
    let root = env_or_default("SITE_ROOT_PATH", "site");
    PathBuf::from(env_or_default(
        "SITE_TEMPLATES_PATH",
        &format!("{root}/templates"),
    ))
}

fn default_public_path() -> PathBuf {
    PathBuf::from(env_or_default("SITE_PUBLIC_PATH", "public"))
}

fn env_or_default(key: &str, default: &str) -> String {
    let raw_value = std::env::var(key).unwrap_or_else(|_| default.to_string());
    // Use the enhanced environment variable expansion from utils crate
    rustelo_utils::env_parsing::expand_shell_variables(&raw_value)
}

fn default_owner() -> String {
    env_or_default("SITE_LEGAL_OWNER", "")
}

fn default_activity() -> String {
    env_or_default("SITE_LEGAL_ACTIVITY", "")
}

fn default_location() -> String {
    env_or_default("SITE_LEGAL_LOCATION", "")
}

fn default_country() -> String {
    env_or_default("SITE_LEGAL_COUNTRY", "")
}

fn default_legal_email() -> String {
    env_or_default("SITE_LEGAL_EMAIL", "legal@")
}

fn default_privacy_email() -> String {
    env_or_default("SITE_PRIVACY_EMAIL", "privacy@")
}

fn default_contact_email() -> String {
    env_or_default("SITE_CONTACT_EMAIL", "hello@")
}

fn default_address() -> String {
    env_or_default("SITE_LEGAL_ADDRESS", "")
}

fn default_logo_light() -> String {
    env_or_default("SITE_LOGO_LIGHT_PATH", "")
}

fn default_logo_dark() -> String {
    env_or_default("SITE_LOGO_DARK_PATH", "")
}

fn default_logo_icon() -> String {
    env_or_default("SITE_LOGO_ICON_PATH", "")
}

impl LegalConfig {
    /// Create LegalConfig from environment variables
    pub fn from_env() -> Self {
        Self {
            owner: default_owner(),
            activity: default_activity(),
            location: default_location(),
            country: default_country(),
            legal_email: default_legal_email(),
            privacy_email: default_privacy_email(),
            contact_email: default_contact_email(),
            address: default_address(),
        }
    }

    /// Get encoded email to prevent spam bots
    pub fn encoded_legal_email(&self) -> String {
        encode_email(&self.legal_email)
    }

    /// Get encoded privacy email to prevent spam bots
    pub fn encoded_privacy_email(&self) -> String {
        encode_email(&self.privacy_email)
    }

    /// Get encoded contact email to prevent spam bots
    pub fn encoded_contact_email(&self) -> String {
        encode_email(&self.contact_email)
    }
}

impl LogoConfig {
    /// Create LogoConfig from environment variables
    pub fn from_env() -> Self {
        Self {
            light_path: default_logo_light(),
            dark_path: default_logo_dark(),
            icon_path: default_logo_icon(),
        }
    }

    /// Check if both light and dark logos are different
    pub fn has_theme_variants(&self) -> bool {
        !self.light_path.is_empty()
            && !self.dark_path.is_empty()
            && self.light_path != self.dark_path
    }

    /// Get the primary logo path (prioritizes light theme)
    pub fn primary_path(&self) -> &str {
        if !self.light_path.is_empty() {
            &self.light_path
        } else if !self.dark_path.is_empty() {
            &self.dark_path
        } else {
            &self.icon_path
        }
    }
}

impl ContentManageConfig {
    /// Create ContentManageConfig from environment variables
    pub fn from_env() -> Self {
        Self {
            root_path: default_content_root(),
            locales_path: default_locales_path(),
            ftl_path: default_ftl_path(),
            emergency_path: default_emergency_path(),
            themes_path: default_themes_path(),
            menus_path: default_menus_path(),
            footer_path: default_footer_path(),
            templates_path: default_templates_path(),
            public_path: default_public_path(),
        }
    }

    /// Get the index path for any content type by language
    /// Uses content-kinds.toml configuration to resolve directory paths
    pub fn content_index_path(&self, content_type: &str, language: &str) -> PathBuf {
        // Get directory from content-kinds configuration
        #[cfg(not(target_arch = "wasm32"))]
        {
            use crate::content::traits::ContentKindRegistry;
            let registry = crate::create_content_kind_registry();

            if let Some(config) = registry.kinds.get(content_type) {
                return self
                    .root_path
                    .join(&config.directory)
                    .join(language)
                    .join("index.json");
            }
        }

        // WASM fallback - try to map content types to directories
        let directory = {
            // First check if we have generated content types available
            let available_types = crate::get_all_content_types();
            if available_types.iter().any(|ct| *ct == content_type) {
                content_type // Use content_type as directory name if it's
                             // configured
            } else {
                // Use content_type as directory name
                content_type
            }
        };

        self.root_path
            .join(directory)
            .join(language)
            .join("index.json")
    }

    /// Get the full path for a theme file
    pub fn theme_path(&self, theme_name: &str) -> PathBuf {
        self.themes_path.join(format!("{theme_name}.toml"))
    }

    /// Get the design system theme path
    pub fn design_system_path(&self) -> PathBuf {
        self.themes_path.join("design-system.toml")
    }

    /// Get the directory path for a content type
    /// Uses content-kinds.toml configuration to resolve directory paths
    pub fn content_type_directory_path(&self, content_type: &str) -> PathBuf {
        // Get directory from content-kinds configuration
        #[cfg(not(target_arch = "wasm32"))]
        {
            use crate::content::traits::ContentKindRegistry;
            let registry = crate::create_content_kind_registry();

            if let Some(config) = registry.kinds.get(content_type) {
                return self.root_path.join(&config.directory);
            }
        }

        // WASM fallback - try to map content types to directories
        let directory = {
            // First check if we have generated content types available
            let available_types = crate::get_all_content_types();
            if available_types.iter().any(|ct| *ct == content_type) {
                content_type // Use content_type as directory name if it's
                             // configured
            } else {
                // Use content_type as directory name
                content_type
            }
        };

        self.root_path.join(directory)
    }

    /// Get the full path for content by type and language
    pub fn content_path(&self, content_type: &str, language: &str, filename: &str) -> PathBuf {
        self.content_type_directory_path(content_type)
            .join(language)
            .join(filename)
    }

    /// Get the manifest path in public directory
    pub fn content_manifest_path(&self) -> PathBuf {
        self.public_path.join("content-manifest.json")
    }
}

/// Encode email address to prevent spam bots
/// Converts characters to HTML entities and uses JavaScript obfuscation
/// techniques
fn encode_email(email: &str) -> String {
    if email.is_empty() {
        return String::new();
    }

    // Simple ROT13-like encoding for basic obfuscation
    let mut encoded = String::new();
    for char in email.chars() {
        let code = char as u32;
        encoded.push_str(&format!("&#{code};"));
    }
    encoded
}

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

    use super::*;

    #[test]
    fn test_env_variable_expansion_in_config() {
        // Set up test environment variables
        env::set_var("TEST_SITE_ROOT", "testsite");
        env::set_var("TEST_CONFIG_PATH", "${TEST_SITE_ROOT}/config");

        // Test that expansion works through env_or_default
        let expanded_path = env_or_default("TEST_CONFIG_PATH", "fallback");
        assert_eq!(expanded_path, "testsite/config");

        // Test fallback behavior
        let fallback_path = env_or_default("NON_EXISTENT_VAR", "default/path");
        assert_eq!(fallback_path, "default/path");

        // Clean up
        env::remove_var("TEST_SITE_ROOT");
        env::remove_var("TEST_CONFIG_PATH");
    }

    #[test]
    fn test_config_struct_creation() {
        // Test that config structs can be created with defaults
        let _legal_config = LegalConfig::default();
        // Config exists and has accessible fields

        let _logo_config = LogoConfig::default();
        // Logo config accessible

        let app_config = AppConfig::default();
        assert!(!app_config.features.is_empty() || app_config.features.is_empty());
        // Features map exists
    }

    #[test]
    fn test_email_encoding() {
        let encoded = encode_email("test@example.com");
        assert!(encoded.contains("&#"));
        assert!(!encoded.contains("@")); // Original @ should be encoded

        let empty_encoded = encode_email("");
        assert_eq!(empty_encoded, "");
    }
}
