//! Unified Application State Management
//!
//! This module provides a centralized state management system that combines
//! language context, content loading, theme management, routing state, and
//! other application-wide concerns into a unified reactive system.

use std::collections::HashMap;

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

use super::content_loader::ReactiveContentLoader;
use super::language::LanguageContext;
use crate::i18n::language_config::get_language_registry;
use crate::routing::utils::RouteInfo;
use crate::theme::{EffectiveTheme, ThemeMode};

/// Unified application state that manages all global reactive state
#[derive(Clone, Debug)]
pub struct AppState {
    /// Language and internationalization state
    pub language: LanguageContext,
    /// Content loading and caching system
    pub content: ReactiveContentLoader,
    /// Current theme configuration
    pub theme: ThemeState,
    /// Current route information
    pub route: RouteState,
    /// User authentication state
    pub auth: AuthState,
    /// UI state (modals, sidebars, etc.)
    pub ui: UIState,
    /// Application metadata and settings
    pub meta: MetaState,
}

/// Theme-related reactive state
#[derive(Clone, Debug)]
pub struct ThemeState {
    /// Current theme mode (light/dark/system)
    pub mode: RwSignal<ThemeMode>,
    /// Effective resolved theme after system detection
    pub effective: RwSignal<EffectiveTheme>,
    /// Whether system theme detection is enabled
    pub system_detection: RwSignal<bool>,
    /// Whether theme persistence is enabled
    pub persistence: RwSignal<bool>,
}

impl ThemeState {
    pub fn new() -> Self {
        Self {
            mode: RwSignal::new(ThemeMode::System),
            effective: RwSignal::new(EffectiveTheme::Light),
            system_detection: RwSignal::new(true),
            persistence: RwSignal::new(true),
        }
    }

    /// Switch to a new theme mode
    pub fn set_theme_mode(&self, new_mode: ThemeMode) {
        tracing::info!("Switching theme mode to: {:?}", new_mode);
        self.mode.set(new_mode);

        // Update effective theme based on system preference if needed
        if new_mode.requires_system_detection() {
            let system_prefers_dark = self.detect_system_preference();
            self.effective.set(new_mode.resolve(system_prefers_dark));
        } else {
            self.effective.set(new_mode.resolve(false));
        }

        // Persist theme preference if enabled
        if self.persistence.get() {
            self.persist_theme_preference(new_mode);
        }
    }

    /// Detect system theme preference
    fn detect_system_preference(&self) -> bool {
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::window;
            if let Some(window) = window() {
                if let Ok(Some(media_query)) = window.match_media("(prefers-color-scheme: dark)") {
                    return media_query.matches();
                }
            }
        }
        false // Default to light theme
    }

    /// Persist theme preference to local storage
    fn persist_theme_preference(&self, theme_mode: ThemeMode) {
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::window;

            if let Some(window) = window() {
                if let Ok(Some(storage)) = window.local_storage() {
                    let _ = storage.set_item("theme_mode", theme_mode.as_str());
                    tracing::debug!("Theme preference persisted: {:?}", theme_mode);
                }
            }
        }

        #[cfg(not(target_arch = "wasm32"))]
        {
            // For SSR, we can't persist to local storage
            tracing::debug!(
                "Would persist theme preference: {:?} (SSR context)",
                theme_mode
            );
        }
    }

    /// Load theme preference from local storage
    pub fn load_persisted_theme(&self) -> Option<ThemeMode> {
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::window;

            if let Some(window) = window() {
                if let Ok(Some(storage)) = window.local_storage() {
                    if let Ok(Some(stored_theme)) = storage.get_item("theme_mode") {
                        return Some(ThemeMode::parse(&stored_theme));
                    }
                }
            }
        }
        None
    }
}

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

/// Route-related reactive state
#[derive(Clone, Debug)]
pub struct RouteState {
    /// Current route information
    pub current: RwSignal<RouteInfo>,
    /// Previous route (for navigation history)
    pub previous: RwSignal<Option<RouteInfo>>,
    /// Navigation loading state
    pub navigating: RwSignal<bool>,
    /// Route transition state
    pub transitioning: RwSignal<bool>,
}

impl RouteState {
    pub fn new() -> Self {
        let initial_route = RouteInfo::from_path("/");

        Self {
            current: RwSignal::new(initial_route),
            previous: RwSignal::new(None),
            navigating: RwSignal::new(false),
            transitioning: RwSignal::new(false),
        }
    }

    /// Navigate to a new route with state management
    pub fn navigate_to(&self, path: &str, language: Option<&str>) {
        let old_route = self.current.get();
        let new_route = if let Some(lang) = language {
            RouteInfo {
                path: path.to_string(),
                language: lang.to_string(),
                clean_path: path.to_string(),
                is_default_language: lang == get_language_registry().get_default_language(),
            }
        } else {
            RouteInfo::from_path(path)
        };

        tracing::info!("Navigating from {} to {}", old_route.path, new_route.path);

        // Set navigation state
        self.navigating.set(true);
        self.transitioning.set(true);

        // Update route state
        self.previous.set(Some(old_route));
        self.current.set(new_route);

        // Clear navigation state after a brief delay (simulating transition)
        // In a real implementation, this would be controlled by the actual navigation
        // For now, we'll just immediately clear the states
        self.navigating.set(false);
        self.transitioning.set(false);
    }

    /// Get the current route path
    pub fn current_path(&self) -> String {
        self.current.get().path
    }

    /// Get the current language from route
    pub fn current_language(&self) -> String {
        self.current.get().language
    }
}

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

/// Authentication-related reactive state
#[derive(Clone, Debug)]
pub struct AuthState {
    /// Whether user is currently authenticated
    pub authenticated: RwSignal<bool>,
    /// Current user information
    pub user: RwSignal<Option<UserInfo>>,
    /// Authentication loading state
    pub loading: RwSignal<bool>,
    /// Authentication error state
    pub error: RwSignal<Option<String>>,
    /// User permissions and roles
    pub permissions: RwSignal<Vec<String>>,
}

/// Simplified user information for state management
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserInfo {
    pub id: String,
    pub email: String,
    pub name: Option<String>,
    pub roles: Vec<String>,
    pub avatar_url: Option<String>,
}

impl AuthState {
    pub fn new() -> Self {
        Self {
            authenticated: RwSignal::new(false),
            user: RwSignal::new(None),
            loading: RwSignal::new(false),
            error: RwSignal::new(None),
            permissions: RwSignal::new(Vec::new()),
        }
    }

    /// Set authenticated user
    pub fn set_user(&self, user: UserInfo) {
        tracing::info!("User authenticated: {}", user.email);

        self.authenticated.set(true);
        self.permissions.set(user.roles.clone());
        self.user.set(Some(user));
        self.error.set(None);
    }

    /// Clear authentication state (logout)
    pub fn clear_auth(&self) {
        tracing::info!("User logged out");

        self.authenticated.set(false);
        self.user.set(None);
        self.permissions.set(Vec::new());
        self.error.set(None);
    }

    /// Check if user has a specific permission/role
    pub fn has_permission(&self, permission: &str) -> bool {
        self.permissions.get().contains(&permission.to_string())
    }

    /// Set authentication error
    pub fn set_error(&self, error: String) {
        tracing::error!("Authentication error: {}", error);
        self.error.set(Some(error));
        self.loading.set(false);
    }
}

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

/// UI-related reactive state (modals, sidebars, etc.)
#[derive(Clone, Debug)]
pub struct UIState {
    /// Global loading state
    pub global_loading: RwSignal<bool>,
    /// Modal visibility states
    pub modals: RwSignal<HashMap<String, bool>>,
    /// Sidebar visibility states
    pub sidebars: RwSignal<HashMap<String, bool>>,
    /// Toast/notification states
    pub notifications: RwSignal<Vec<Notification>>,
    /// Mobile menu state
    pub mobile_menu_open: RwSignal<bool>,
    /// Search overlay state
    pub search_open: RwSignal<bool>,
}

/// Notification/toast message
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Notification {
    pub id: String,
    pub message: String,
    pub level: NotificationLevel,
    pub duration: Option<u32>, // Duration in milliseconds
    pub timestamp: u64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NotificationLevel {
    Info,
    Success,
    Warning,
    Error,
}

impl UIState {
    pub fn new() -> Self {
        Self {
            global_loading: RwSignal::new(false),
            modals: RwSignal::new(HashMap::new()),
            sidebars: RwSignal::new(HashMap::new()),
            notifications: RwSignal::new(Vec::new()),
            mobile_menu_open: RwSignal::new(false),
            search_open: RwSignal::new(false),
        }
    }

    /// Show a modal by name
    pub fn show_modal(&self, modal_name: &str) {
        let mut modals = self.modals.get();
        modals.insert(modal_name.to_string(), true);
        self.modals.set(modals);
        tracing::debug!("Modal '{}' opened", modal_name);
    }

    /// Hide a modal by name
    pub fn hide_modal(&self, modal_name: &str) {
        let mut modals = self.modals.get();
        modals.insert(modal_name.to_string(), false);
        self.modals.set(modals);
        tracing::debug!("Modal '{}' closed", modal_name);
    }

    /// Check if modal is visible
    pub fn is_modal_visible(&self, modal_name: &str) -> bool {
        self.modals.get().get(modal_name).copied().unwrap_or(false)
    }

    /// Add a notification
    pub fn add_notification(
        &self,
        message: String,
        level: NotificationLevel,
        duration: Option<u32>,
    ) {
        let notification = Notification {
            id: uuid::Uuid::new_v4().to_string(),
            message,
            level,
            duration,
            timestamp: {
                #[cfg(target_arch = "wasm32")]
                {
                    use web_sys::js_sys::Date;
                    (Date::now() / 1000.0) as u64
                }
                #[cfg(not(target_arch = "wasm32"))]
                {
                    chrono::Utc::now().timestamp() as u64
                }
            },
        };

        let mut notifications = self.notifications.get();
        notifications.push(notification);
        self.notifications.set(notifications);
    }

    /// Remove a notification by ID
    pub fn remove_notification(&self, id: &str) {
        let mut notifications = self.notifications.get();
        notifications.retain(|n| n.id != id);
        self.notifications.set(notifications);
    }

    /// Toggle mobile menu
    pub fn toggle_mobile_menu(&self) {
        let current = self.mobile_menu_open.get();
        self.mobile_menu_open.set(!current);
        tracing::debug!("Mobile menu toggled: {}", !current);
    }
}

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

/// Application metadata and settings
#[derive(Clone, Debug)]
pub struct MetaState {
    /// Application version
    pub version: RwSignal<String>,
    /// Environment (development, staging, production)
    pub environment: RwSignal<String>,
    /// Application title
    pub title: RwSignal<String>,
    /// Meta description
    pub description: RwSignal<String>,
    /// Keywords for SEO
    pub keywords: RwSignal<Vec<String>>,
    /// Canonical URL
    pub canonical_url: RwSignal<Option<String>>,
    /// Social media metadata
    pub social_meta: RwSignal<SocialMeta>,
}

/// Social media metadata
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SocialMeta {
    pub og_title: Option<String>,
    pub og_description: Option<String>,
    pub og_image: Option<String>,
    pub og_url: Option<String>,
    pub twitter_card: Option<String>,
    pub twitter_site: Option<String>,
}

impl MetaState {
    pub fn new() -> Self {
        Self {
            version: RwSignal::new("0.1.0".to_string()),
            environment: RwSignal::new("development".to_string()),
            title: RwSignal::new("Rustelo App".to_string()),
            description: RwSignal::new("A modern Rust web application".to_string()),
            keywords: RwSignal::new(vec![
                "rust".to_string(),
                "web".to_string(),
                "leptos".to_string(),
            ]),
            canonical_url: RwSignal::new(None),
            social_meta: RwSignal::new(SocialMeta::default()),
        }
    }

    /// Update page metadata
    pub fn update_page_meta(&self, title: String, description: String, keywords: Vec<String>) {
        self.title.set(title);
        self.description.set(description);
        self.keywords.set(keywords);
        tracing::debug!("Page metadata updated");
    }

    /// Set canonical URL
    pub fn set_canonical_url(&self, url: String) {
        self.canonical_url.set(Some(url));
    }

    /// Update social media metadata
    pub fn update_social_meta(&self, meta: SocialMeta) {
        self.social_meta.set(meta);
    }
}

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

impl AppState {
    /// Create new unified application state
    pub fn new() -> Self {
        let language = LanguageContext::new();
        let content = ReactiveContentLoader::new();
        let theme = ThemeState::new();
        let route = RouteState::new();
        let auth = AuthState::new();
        let ui = UIState::new();
        let meta = MetaState::new();

        Self {
            language,
            content,
            theme,
            route,
            auth,
            ui,
            meta,
        }
    }

    /// Initialize application with environment and settings
    pub fn initialize(&self, config: AppConfig) {
        // Set environment info
        self.meta.version.set(config.version);
        self.meta.environment.set(config.environment);

        // Initialize theme from config or persisted settings
        if let Some(persisted_theme) = self.theme.load_persisted_theme() {
            self.theme.set_theme_mode(persisted_theme);
        } else if let Some(default_theme) = config.default_theme {
            self.theme.set_theme_mode(default_theme);
        }

        // Preload content for supported languages
        let lang_refs: Vec<&str> = config
            .supported_languages
            .iter()
            .map(|s| s.as_str())
            .collect();
        self.content.preload_languages(&lang_refs);

        // Set initial language if provided
        if let Some(initial_lang) = config.initial_language {
            self.language.switch_language(&initial_lang);
        }

        tracing::info!("Application state initialized");
    }

    /// Reactive language switching that updates all related state
    pub fn switch_language(&self, new_language: &str) {
        // Update language context
        self.language.switch_language(new_language);

        // Update route if needed to reflect language change
        let current_route = self.route.current.get();
        if current_route.language != new_language {
            let localized_path =
                crate::routing::utils::get_localized_route(&current_route.clean_path, new_language);
            self.route.navigate_to(&localized_path, Some(new_language));
        }

        // Preload content for the new language if not already cached
        if self.content.get_i18n_content(new_language).is_none() {
            self.content.load_i18n_content(new_language);
        }
    }

    /// Get current application state summary for debugging
    pub fn get_state_summary(&self) -> AppStateSummary {
        AppStateSummary {
            current_language: self.language.get_current(),
            current_theme: self.theme.mode.get(),
            current_route: self.route.current_path(),
            authenticated: self.auth.authenticated.get(),
            content_cache_stats: self.content.get_cache_stats(),
            notifications_count: self.ui.notifications.get().len(),
        }
    }
}

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

/// Configuration for initializing the application state
#[derive(Debug, Clone)]
pub struct AppConfig {
    pub version: String,
    pub environment: String,
    pub supported_languages: Vec<String>,
    pub initial_language: Option<String>,
    pub default_theme: Option<ThemeMode>,
}

impl Default for AppConfig {
    fn default() -> Self {
        // Use dynamic language discovery instead of hardcoded languages
        let language_registry = get_language_registry();
        let supported_languages = language_registry.get_available_languages();
        let default_language = language_registry.get_default_language().to_string();

        Self {
            version: "0.1.0".to_string(),
            environment: "development".to_string(),
            supported_languages,
            initial_language: Some(default_language),
            default_theme: Some(ThemeMode::System),
        }
    }
}

/// Summary of application state for debugging
#[derive(Debug, Clone, Serialize)]
pub struct AppStateSummary {
    pub current_language: String,
    pub current_theme: ThemeMode,
    pub current_route: String,
    pub authenticated: bool,
    pub content_cache_stats: super::content_loader::ContentCacheStats,
    pub notifications_count: usize,
}

/// Application state provider component
#[component]
pub fn AppStateProvider(
    #[prop(default = AppConfig::default())] config: AppConfig,
    children: Children,
) -> impl IntoView {
    let app_state = AppState::new();

    // Initialize with configuration
    app_state.initialize(config);

    provide_context(app_state);
    children()
}

/// Hook to use application state in components
pub fn use_app_state() -> AppState {
    expect_context::<AppState>()
}

/// Hook to get application state summary reactively
pub fn use_app_state_summary() -> Signal<AppStateSummary> {
    let app_state = use_app_state();
    Signal::derive(move || app_state.get_state_summary())
}

/// Hook for theme management
pub fn use_theme() -> ThemeState {
    use_app_state().theme
}

/// Hook for route management
pub fn use_route_state() -> RouteState {
    use_app_state().route
}

/// Hook for authentication state
pub fn use_auth_state() -> AuthState {
    use_app_state().auth
}

/// Hook for UI state management
pub fn use_ui_state() -> UIState {
    use_app_state().ui
}

/// Hook for metadata management
pub fn use_meta_state() -> MetaState {
    use_app_state().meta
}

#[cfg(test)]
mod tests {
    use leptos::prelude::Owner;

    use super::*;

    #[test]
    fn test_app_state_creation() {
        Owner::new().with(|| {
            let state = AppState::new();
            let language_registry = get_language_registry();
            let default_language = language_registry.get_default_language();

            // Check initial values
            assert_eq!(state.language.get_current(), default_language);
            assert_eq!(state.theme.mode.get(), ThemeMode::System);
            assert!(!state.auth.authenticated.get());
            assert_eq!(state.route.current_path(), "/");
        });
    }

    #[test]
    fn test_theme_state_switching() {
        Owner::new().with(|| {
            let theme_state = ThemeState::new();

            // Switch to dark theme
            theme_state.set_theme_mode(ThemeMode::Dark);
            assert_eq!(theme_state.mode.get(), ThemeMode::Dark);
            assert_eq!(theme_state.effective.get(), EffectiveTheme::Dark);

            // Switch to light theme
            theme_state.set_theme_mode(ThemeMode::Light);
            assert_eq!(theme_state.mode.get(), ThemeMode::Light);
            assert_eq!(theme_state.effective.get(), EffectiveTheme::Light);
        });
    }

    #[test]
    fn test_notification_management() {
        Owner::new().with(|| {
            let ui_state = UIState::new();

            // Add notification
            ui_state.add_notification(
                "Test message".to_string(),
                NotificationLevel::Info,
                Some(5000),
            );

            let notifications = ui_state.notifications.get();
            assert_eq!(notifications.len(), 1);
            assert_eq!(notifications[0].message, "Test message");

            // Remove notification
            let notification_id = notifications[0].id.clone();
            ui_state.remove_notification(&notification_id);

            assert_eq!(ui_state.notifications.get().len(), 0);
        });
    }

    #[test]
    fn test_auth_state_management() {
        Owner::new().with(|| {
            let auth_state = AuthState::new();

            // Initially not authenticated
            assert!(!auth_state.authenticated.get());

            // Set user
            let user = UserInfo {
                id: "test-id".to_string(),
                email: "test@example.com".to_string(),
                name: Some("Test User".to_string()),
                roles: vec!["user".to_string()],
                avatar_url: None,
            };

            auth_state.set_user(user);
            assert!(auth_state.authenticated.get());
            assert!(auth_state.has_permission("user"));

            // Clear auth
            auth_state.clear_auth();
            assert!(!auth_state.authenticated.get());
            assert!(!auth_state.has_permission("user"));
        });
    }

    #[test]
    fn test_app_config_default() {
        let config = AppConfig::default();
        let language_registry = get_language_registry();
        let default_language = language_registry.get_default_language();

        assert_eq!(config.version, "0.1.0");
        assert_eq!(config.environment, "development");
        assert!(config
            .supported_languages
            .contains(&default_language.to_string()));
        assert!(!config.supported_languages.is_empty());
    }
}
