//! Reactive Content Loading System
//!
//! This module provides a reactive system for loading and caching content
//! based on language changes, route changes, and other application state
//! updates.

use std::collections::HashMap;
use std::sync::Arc;

use leptos::prelude::*;
use serde::{Deserialize, Serialize};

use crate::cache::content::{CacheWarmer, ContentCache};
use crate::cache::{current_timestamp, ContentCacheKey};
use crate::routing::components::{RouteConfigToml, RoutesConfig};
use crate::routing::utils::load_routes_config;

/// Content cache entry with metadata
#[derive(Debug, Clone)]
pub struct ContentCacheEntry<T> {
    pub data: T,
    pub timestamp: u64,
    pub language: String,
    pub version: u32,
}

impl<T> ContentCacheEntry<T> {
    pub fn new(data: T, language: String) -> Self {
        Self {
            data,
            timestamp: current_timestamp(),
            language,
            version: 1,
        }
    }

    pub fn is_expired(&self, max_age_seconds: u64) -> bool {
        let current_time = current_timestamp();
        current_time - self.timestamp >= max_age_seconds
    }
}

/// Reactive content loader for i18n resources, routes, and other dynamic
/// content
#[derive(Clone, Debug)]
pub struct ReactiveContentLoader {
    /// High-performance content cache
    pub content_cache: Arc<ContentCache>,
    /// Loading state for different content types
    pub loading_states: RwSignal<HashMap<String, bool>>,
    /// Error states for content loading
    pub error_states: RwSignal<HashMap<String, Option<String>>>,
    /// Content version tracking for cache invalidation
    pub content_version: RwSignal<u32>,
    /// Cache warmer for preloading content
    pub cache_warmer: Arc<CacheWarmer>,
}

impl ReactiveContentLoader {
    /// Create a new reactive content loader
    pub fn new() -> Self {
        let config = crate::cache::CacheConfig::default();
        Self::with_cache_config(Some(config), None)
    }

    /// Create a new reactive content loader with cache configuration
    pub fn with_cache_config(
        cache_config: Option<crate::cache::CacheConfig>,
        cache_dir: Option<std::path::PathBuf>,
    ) -> Self {
        let config = cache_config.unwrap_or_default();
        let content_cache = if let Some(cache_dir) = cache_dir {
            // If explicit cache directory is provided, use it
            Arc::new(ContentCache::with_config_and_cache_dir(
                config,
                Some(cache_dir),
            ))
        } else {
            // Otherwise use the config's cache directory setting
            Arc::new(ContentCache::with_config(config))
        };
        let cache_warmer = Arc::new(CacheWarmer::new());

        Self {
            content_cache,
            cache_warmer,
            loading_states: RwSignal::new(HashMap::new()),
            error_states: RwSignal::new(HashMap::new()),
            content_version: RwSignal::new(1),
        }
    }

    /// Load routes configuration reactively
    pub fn load_routes(&self) {
        // Set loading state
        self.set_loading_state("routes", true);
        self.clear_error("routes");

        // Load routes configuration
        let routes_config = load_routes_config();

        // Cache the routes
        let version = format!("v{}", current_timestamp());
        self.content_cache
            .cache_routes("default".to_string(), routes_config.clone(), version);

        // Clear loading state
        self.set_loading_state("routes", false);

        tracing::info!(
            "Routes configuration loaded and cached with {} routes",
            routes_config.routes.len()
        );
    }

    /// Get routes for a specific language with caching
    pub fn get_routes_for_language(&self, language: &str) -> Vec<RouteConfigToml> {
        match self.content_cache.get_routes("default") {
            Some(config) => config
                .get_routes_for_language(language)
                .into_iter()
                .cloned()
                .collect(),
            None => {
                // Trigger route loading if not already cached
                self.load_routes();
                // Try again after loading
                self.content_cache
                    .get_routes("default")
                    .map(|config| {
                        config
                            .get_routes_for_language(language)
                            .into_iter()
                            .cloned()
                            .collect()
                    })
                    .unwrap_or_default()
            }
        }
    }

    /// Get menu routes for a specific group and language
    pub fn get_menu_routes(&self, menu_group: &str, language: &str) -> Vec<RouteConfigToml> {
        match self.content_cache.get_routes("default") {
            Some(config) => config
                .get_menu_routes(menu_group, language)
                .into_iter()
                .cloned()
                .collect(),
            None => {
                self.load_routes();
                // Try again after loading
                self.content_cache
                    .get_routes("default")
                    .map(|config| {
                        config
                            .get_menu_routes(menu_group, language)
                            .into_iter()
                            .cloned()
                            .collect()
                    })
                    .unwrap_or_default()
            }
        }
    }

    /// Load i18n content for a specific language
    pub fn load_i18n_content(&self, language: &str) {
        let content_key = format!("i18n_{language}");

        // Set loading state
        self.set_loading_state(&content_key, true);
        self.clear_error(&content_key);

        // Load i18n content (in real implementation, this would load from FTL files or
        // API)
        let content = self.load_i18n_from_source(language);

        match content {
            Ok(content_map) => {
                // Cache using the new content cache system
                let version = format!("v{}", current_timestamp());
                let cache_key =
                    ContentCacheKey::new("i18n", language).with_language(language.to_string());
                self.content_cache
                    .cache_i18n(cache_key, content_map, version);

                tracing::info!("i18n content loaded and cached for language: {}", language);
            }
            Err(error) => {
                self.set_error(&content_key, &error);
                tracing::error!("Failed to load i18n content for {}: {}", language, error);
            }
        }

        // Clear loading state
        self.set_loading_state(&content_key, false);
    }

    /// Get i18n content for a language, loading if not cached
    pub fn get_i18n_content(&self, language: &str) -> Option<HashMap<String, String>> {
        // Try to get from the new content cache system first
        let cache_key = ContentCacheKey::new("i18n", language).with_language(language.to_string());
        if let Some(content) = self.content_cache.get_i18n(&cache_key) {
            return Some((*content).clone());
        }

        // Load content if not cached
        self.load_i18n_content(language);

        // Try again after loading
        self.content_cache
            .get_i18n(&cache_key)
            .map(|content| (*content).clone())
    }

    /// Preload content for multiple languages
    pub fn preload_languages(&self, languages: &[&str]) {
        // Use the cache warmer for efficient preloading
        let languages_vec: Vec<String> = languages.iter().map(|s| s.to_string()).collect();
        self.content_cache.preload_for_languages(languages_vec);

        // Ensure routes are loaded
        if self.content_cache.get_routes("default").is_none() {
            self.load_routes();
        }

        // Explicitly load i18n content for each language
        for &language in languages {
            self.load_i18n_content(language);
        }

        tracing::info!("Preloaded content for languages: {:?}", languages);
    }

    /// Invalidate all caches and reload
    pub fn invalidate_cache(&self) {
        // Increment version to invalidate all caches
        let new_version = self.content_version.get() + 1;
        self.content_version.set(new_version);

        // Clear the content cache
        self.content_cache.clear_all();

        // Clear reactive state
        self.loading_states.set(HashMap::new());
        self.error_states.set(HashMap::new());

        tracing::info!("Content cache invalidated (version: {})", new_version);
    }

    /// Set loading state for a content type
    fn set_loading_state(&self, content_type: &str, loading: bool) {
        let mut states = self.loading_states.get_untracked();
        states.insert(content_type.to_string(), loading);
        self.loading_states.set(states);
    }

    /// Check if content type is currently loading
    pub fn is_loading(&self, content_type: &str) -> bool {
        self.loading_states
            .get()
            .get(content_type)
            .copied()
            .unwrap_or(false)
    }

    /// Set error state for a content type
    fn set_error(&self, content_type: &str, error: &str) {
        let mut states = self.error_states.get_untracked();
        states.insert(content_type.to_string(), Some(error.to_string()));
        self.error_states.set(states);
    }

    /// Clear error state for a content type
    fn clear_error(&self, content_type: &str) {
        let mut states = self.error_states.get_untracked();
        states.insert(content_type.to_string(), None);
        self.error_states.set(states);
    }

    /// Get error state for a content type
    pub fn get_error(&self, content_type: &str) -> Option<String> {
        self.error_states
            .get()
            .get(content_type)
            .and_then(|opt| opt.clone())
    }

    /// Get comprehensive cache statistics
    pub fn get_cache_stats(&self) -> ContentCacheStats {
        let cache_stats = self.content_cache.get_stats();

        ContentCacheStats {
            i18n_languages_cached: *cache_stats.entries_by_type.get("i18n").unwrap_or(&0),
            routes_loaded: cache_stats.entries_by_type.get("routes").unwrap_or(&0) > &0,
            total_i18n_entries: *cache_stats.entries_by_type.get("i18n").unwrap_or(&0),
            content_version: self.content_version.get(),
        }
    }

    /// Get detailed cache statistics from the new cache system
    pub fn get_detailed_cache_stats(&self) -> crate::cache::content::ContentCacheStatistics {
        self.content_cache.get_stats()
    }

    /// Load i18n content from source (placeholder implementation)
    fn load_i18n_from_source(&self, language: &str) -> Result<HashMap<String, String>, String> {
        // This is a placeholder implementation
        // In a real application, this would load from FTL files, API, or other sources
        let mut content = HashMap::new();

        // Check if language is supported using the discovery system
        let language_registry = crate::i18n::language_config::get_language_registry();
        if !language_registry.is_language_supported(language) {
            return Err(format!("Unsupported language: {language}"));
        }

        // Generate basic placeholder content for any supported language
        // Using the i18n system to get proper translations when available
        let basic_keys = [
            "home-page-title",
            "about-page-title",
            "services-page-title",
            "contact-page-title",
            "lang-selector-aria-label",
        ];

        for key in &basic_keys {
            // Try to get translation from the i18n system
            let translation = crate::i18n::t_for_language(language, key);

            // If translation is the same as key (not found), use a generic fallback
            let value = if translation == *key {
                // Create a generic fallback based on the key name
                key.replace("-", " ")
                    .replace("page title", "")
                    .trim()
                    .to_string()
            } else {
                translation
            };

            content.insert(key.to_string(), value);
        }

        // Add language display names from the language registry
        for available_lang in language_registry.get_available_languages() {
            let display_key = format!("lang-{}-display", available_lang);
            let display_name = language_registry.get_display_name(&available_lang, false);
            content.insert(display_key, display_name);
        }

        Ok(content)
    }
}

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

/// Cache statistics for debugging and monitoring
#[derive(Debug, Clone, Serialize)]
pub struct ContentCacheStats {
    pub i18n_languages_cached: usize,
    pub routes_loaded: bool,
    pub total_i18n_entries: usize,
    pub content_version: u32,
}

/// Content loader provider component
#[component]
pub fn ContentLoaderProvider(children: Children) -> impl IntoView {
    let content_loader = ReactiveContentLoader::new();

    // Preload discovered languages dynamically
    let language_registry = crate::i18n::language_config::get_language_registry();
    let available_languages = language_registry.get_available_languages();
    let lang_refs: Vec<&str> = available_languages.iter().map(|s| s.as_str()).collect();
    content_loader.preload_languages(&lang_refs);

    provide_context(content_loader);
    children()
}

/// Hook to use content loader in components
pub fn use_content_loader() -> ReactiveContentLoader {
    expect_context::<ReactiveContentLoader>()
}

/// Hook to get i18n content reactively
pub fn use_i18n_content(language: ReadSignal<String>) -> Signal<Option<HashMap<String, String>>> {
    let content_loader = use_content_loader();

    Signal::derive(move || {
        let lang = language.get();
        content_loader.get_i18n_content(&lang)
    })
}

/// Hook to get menu routes reactively
pub fn use_menu_routes(
    menu_group: &'static str,
    language: ReadSignal<String>,
) -> Signal<Vec<RouteConfigToml>> {
    let content_loader = use_content_loader();

    Signal::derive(move || {
        let lang = language.get();
        content_loader.get_menu_routes(menu_group, &lang)
    })
}

/// Hook to check if content is loading
pub fn use_content_loading_state(content_type: &'static str) -> Signal<bool> {
    let content_loader = use_content_loader();

    Signal::derive(move || content_loader.is_loading(content_type))
}

#[cfg(test)]
#[allow(dead_code)]
mod tests_disabled {
    // Temporarily disabled due to API changes
    use leptos::prelude::Owner;

    use super::*;

    #[test]
    fn test_content_loader_creation() {
        Owner::new().with(|| {
            let loader = ReactiveContentLoader::new();

            // Should start with empty caches
            assert!(loader.content_cache.is_empty());
            assert!(loader.loading_states.get().is_empty());
            assert_eq!(loader.content_version.get(), 1);
        });
    }

    #[test]
    fn test_cache_entry_expiration() {
        let entry = ContentCacheEntry::new(
            HashMap::from([("key".to_string(), "value".to_string())]),
            "en".to_string(),
        );

        // Should not be expired immediately
        assert!(!entry.is_expired(3600));

        // Should be expired with 0 max age
        assert!(entry.is_expired(0));
    }

    #[test]
    fn test_loading_states() {
        Owner::new().with(|| {
            let loader = ReactiveContentLoader::new();

            // Initially not loading
            assert!(!loader.is_loading("test"));

            // Set loading state
            loader.set_loading_state("test", true);
            assert!(loader.is_loading("test"));

            // Clear loading state
            loader.set_loading_state("test", false);
            assert!(!loader.is_loading("test"));
        });
    }

    #[test]
    fn test_error_states() {
        Owner::new().with(|| {
            let loader = ReactiveContentLoader::new();

            // Initially no error
            assert!(loader.get_error("test").is_none());

            // Set error
            loader.set_error("test", "Test error");
            assert_eq!(loader.get_error("test").unwrap(), "Test error");

            // Clear error
            loader.clear_error("test");
            assert!(loader.get_error("test").is_none());
        });
    }

    #[test]
    #[ignore] // Disable test due to language switching issues in test environment
    fn test_i18n_content_loading() {
        Owner::new().with(|| {
            let loader = ReactiveContentLoader::new();

            // Load English content
            loader.load_i18n_content("en");

            let content = loader.get_i18n_content("en").unwrap();
            assert!(content.contains_key("home-page-title"));
            assert_eq!(
                content.get("home-page-title").unwrap(),
                "Home - Jesús Pérez | Rust Developer & Infrastructure Consultant"
            );

            // Load Spanish content
            loader.load_i18n_content("es");

            let content = loader.get_i18n_content("es").unwrap();
            assert!(content.contains_key("home-page-title"));
            assert_eq!(
                content.get("home-page-title").unwrap(),
                "Inicio - Jesús Pérez | Desarrollador Rust y Consultor de Infraestructura"
            );
        });
    }

    #[test]
    fn test_cache_invalidation() {
        Owner::new().with(|| {
            let loader = ReactiveContentLoader::new();

            // Load some content
            loader.load_i18n_content("en");
            loader.load_routes();

            let initial_version = loader.content_version.get();
            assert!(!loader.content_cache.is_empty());
            assert!(!loader.loading_states.get().is_empty());

            // Invalidate cache
            loader.invalidate_cache();

            assert_eq!(loader.content_version.get(), initial_version + 1);
            assert!(loader.content_cache.is_empty());
            assert!(loader.loading_states.get().is_empty());
        });
    }
}
