//! Resource Management System
//!
//! Provides clean, idiomatic resource loading for both WASM and native
//! environments using modern Rust patterns and build-time resource discovery.

use std::collections::HashMap;

use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

/// Resource loading errors
#[derive(Debug, thiserror::Error)]
pub enum ResourceError {
    #[error("Resource not found: {path}")]
    NotFound { path: String },
    #[error("Failed to parse TOML: {source}")]
    TomlParse { source: toml::de::Error },
    #[error("IO error: {source}")]
    Io { source: std::io::Error },
}

/// Resource type enum for type-safe resource loading
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ResourceType {
    Menu,
    Footer,
    Theme,
    Translation,
}

/// Resource metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceMetadata {
    pub resource_type: String,
    pub language: Option<String>,
    pub theme_name: Option<String>,
    pub version: String,
}

/// Trait for resources that can be loaded dynamically
pub trait LoadableResource: for<'de> Deserialize<'de> + Sized {
    const RESOURCE_TYPE: ResourceType;

    fn from_toml_str(content: &str) -> Result<Self, ResourceError> {
        toml::from_str(content).map_err(|source| ResourceError::TomlParse { source })
    }
}

/// Resource registry for managing embedded resources
pub struct ResourceRegistry {
    resources: HashMap<String, &'static str>,
}

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

impl ResourceRegistry {
    /// Create a new resource registry with embedded resources
    pub fn new() -> Self {
        #[cfg(target_arch = "wasm32")]
        {
            #[allow(unused_variables)]
            let mut registry = Self {
                resources: HashMap::new(),
            };
            registry.register_embedded_resources();
            registry
        }

        #[cfg(not(target_arch = "wasm32"))]
        {
            Self {
                resources: HashMap::new(),
            }
        }
    }

    /// Register embedded resources (WASM only)
    #[cfg(target_arch = "wasm32")]
    fn register_embedded_resources(&mut self) {
        // Load from the auto-generated resource registry
        // For WASM builds, we need to populate the resources HashMap
        // This is handled by the resource_loader module instead
    }

    /// Load a resource by path
    pub fn load<T: LoadableResource>(&self, path: &str) -> Result<T, ResourceError> {
        #[cfg(target_arch = "wasm32")]
        {
            // Use embedded resources for WASM
            let content = self
                .resources
                .get(path)
                .ok_or_else(|| ResourceError::NotFound {
                    path: path.to_string(),
                })?;
            T::from_toml_str(content)
        }

        #[cfg(not(target_arch = "wasm32"))]
        {
            // Load from filesystem for native
            let content =
                std::fs::read_to_string(path).map_err(|source| ResourceError::Io { source })?;
            T::from_toml_str(&content)
        }
    }

    /// Load resource with language fallback
    pub fn load_with_language<T: LoadableResource>(
        &self,
        base_path: &str,
        language: &str,
    ) -> Result<T, ResourceError> {
        // Try specific language first
        let lang_path = format!("{base_path}/{language}.toml");
        if let Ok(resource) = self.load::<T>(&lang_path) {
            return Ok(resource);
        }

        // Fallback to English
        let en_path = format!("{base_path}/en.toml");
        if let Ok(resource) = self.load::<T>(&en_path) {
            return Ok(resource);
        }

        // Last resort: try default
        let default_path = format!("{base_path}/default.toml");
        self.load::<T>(&default_path)
    }

    /// Load theme with name
    pub fn load_theme<T: LoadableResource>(&self, theme_name: &str) -> Result<T, ResourceError> {
        let config = crate::config::ContentManageConfig::from_env();
        let path = config.theme_path(theme_name);
        self.load::<T>(&path.to_string_lossy())
    }

    /// List available resources of a type
    pub fn list_resources(&self, resource_type: ResourceType) -> Vec<String> {
        let config = crate::config::ContentManageConfig::from_env();
        let prefix = match resource_type {
            ResourceType::Menu => config.menus_path.to_string_lossy().to_string() + "/",
            ResourceType::Footer => config.footer_path.to_string_lossy().to_string() + "/",
            ResourceType::Theme => config.themes_path.to_string_lossy().to_string() + "/",
            ResourceType::Translation => config.locales_path.to_string_lossy().to_string() + "/",
        };

        self.resources
            .keys()
            .filter(|path| path.starts_with(&prefix))
            .map(|path| path.to_string())
            .collect()
    }
}

/// Global resource registry instance using once_cell for thread safety
static RESOURCE_REGISTRY: Lazy<ResourceRegistry> = Lazy::new(ResourceRegistry::new);

/// Get the global resource registry
pub fn get_resource_registry() -> &'static ResourceRegistry {
    &RESOURCE_REGISTRY
}

/// Convenient resource loading functions
pub mod loaders {
    use super::*;
    use crate::{DesignSystemConfig, FooterConfig, MenuConfig, ThemeConfig};

    impl LoadableResource for MenuConfig {
        const RESOURCE_TYPE: ResourceType = ResourceType::Menu;
    }

    impl LoadableResource for FooterConfig {
        const RESOURCE_TYPE: ResourceType = ResourceType::Footer;
    }

    impl LoadableResource for ThemeConfig {
        const RESOURCE_TYPE: ResourceType = ResourceType::Theme;
    }

    impl LoadableResource for DesignSystemConfig {
        const RESOURCE_TYPE: ResourceType = ResourceType::Theme;
    }

    /// Load menu for language with fallback
    pub fn load_menu(language: &str) -> Result<MenuConfig, ResourceError> {
        let config = crate::config::ContentManageConfig::from_env();
        get_resource_registry().load_with_language(&config.menus_path.to_string_lossy(), language)
    }

    /// Load footer for language with fallback
    pub fn load_footer(language: &str) -> Result<FooterConfig, ResourceError> {
        let config = crate::config::ContentManageConfig::from_env();
        get_resource_registry().load_with_language(&config.footer_path.to_string_lossy(), language)
    }

    /// Load theme by name
    pub fn load_theme(theme_name: &str) -> Result<ThemeConfig, ResourceError> {
        get_resource_registry().load_theme(theme_name)
    }

    /// Load design system configuration
    pub fn load_design_system() -> Result<DesignSystemConfig, ResourceError> {
        let config = crate::config::ContentManageConfig::from_env();
        get_resource_registry().load(&config.design_system_path().to_string_lossy())
    }

    /// Get available themes
    pub fn available_themes() -> Vec<String> {
        let config = crate::config::ContentManageConfig::from_env();
        let themes_prefix = config.themes_path.to_string_lossy().to_string() + "/";
        get_resource_registry()
            .list_resources(ResourceType::Theme)
            .into_iter()
            .filter_map(|path| {
                path.strip_prefix(&themes_prefix)
                    .and_then(|name| name.strip_suffix(".toml"))
                    .map(|name| name.to_string())
            })
            .collect()
    }

    /// Get available languages for a resource type
    pub fn available_languages(resource_type: ResourceType) -> Vec<String> {
        let config = crate::config::ContentManageConfig::from_env();
        get_resource_registry()
            .list_resources(resource_type)
            .into_iter()
            .filter_map(|path| {
                let prefix = match resource_type {
                    ResourceType::Menu => config.menus_path.to_string_lossy().to_string() + "/",
                    ResourceType::Footer => config.footer_path.to_string_lossy().to_string() + "/",
                    _ => return None,
                };

                path.strip_prefix(&prefix)
                    .and_then(|name| name.strip_suffix(".toml"))
                    .map(|name| name.to_string())
            })
            .collect()
    }
}

/// Resource caching for performance
#[cfg(feature = "cache")]
pub mod cache {
    use std::sync::{Arc, Mutex};

    use super::*;

    type CacheKey = String;
    type CacheValue = Arc<dyn std::any::Any + Send + Sync>;

    pub struct ResourceCache {
        cache: Arc<Mutex<HashMap<CacheKey, CacheValue>>>,
    }

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

    impl ResourceCache {
        pub fn new() -> Self {
            Self {
                cache: Arc::new(Mutex::new(HashMap::new())),
            }
        }

        pub fn get_or_load<T: LoadableResource + Clone + Send + Sync + 'static>(
            &self,
            key: &str,
            loader: impl FnOnce() -> Result<T, ResourceError>,
        ) -> Result<T, ResourceError> {
            let mut cache = self.cache.lock().map_err(|_| ResourceError::NotFound {
                path: "Cache lock poisoned".to_string(),
            })?;

            if let Some(cached) = cache.get(key) {
                if let Some(typed) = cached.downcast_ref::<T>() {
                    return Ok(typed.clone());
                }
            }

            let resource = loader()?;
            cache.insert(key.to_string(), Arc::new(resource.clone()));
            Ok(resource)
        }
    }

    static RESOURCE_CACHE: std::sync::OnceLock<ResourceCache> = std::sync::OnceLock::new();

    pub fn get_cache() -> &'static ResourceCache {
        RESOURCE_CACHE.get_or_init(ResourceCache::new)
    }
}

/// Hot reloading support for development
#[cfg(all(feature = "hot-reload", not(target_arch = "wasm32")))]
pub mod hot_reload {
    use std::sync::mpsc::channel;
    use std::time::Duration;

    use notify::{RecursiveMode, Watcher};

    use super::*;

    pub struct HotReloadWatcher {
        _watcher: Box<dyn Watcher>,
    }

    impl HotReloadWatcher {
        pub fn new<F>(callback: F) -> Result<Self, Box<dyn std::error::Error>>
        where
            F: Fn(&str) + Send + 'static,
        {
            let (tx, rx) = channel::<Result<notify::Event, notify::Error>>();
            let mut watcher = notify::recommended_watcher(move |res| {
                if let Ok(event) = res {
                    let _ = tx.send(Ok(event));
                }
            })?;

            watcher.watch(std::path::Path::new("content"), RecursiveMode::Recursive)?;

            std::thread::spawn(move || {
                while let Ok(event) = rx.recv() {
                    if let Ok(event) = event {
                        if let Some(path) = event.paths.first() {
                            if let Some(path_str) = path.to_str() {
                                callback(path_str);
                            }
                        }
                    }
                }
            });

            Ok(Self {
                _watcher: Box::new(watcher),
            })
        }
    }
}
