//! Navigation state management for URL handling without page reloads
//!
//! This module provides a navigation system that integrates with the routing
//! engine and language context to enable seamless navigation without page
//! reloads.

use std::collections::HashMap;

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

use crate::i18n::UnifiedI18n;
use crate::routing::engine::{
    get_language_switch_url, get_routing_engine, resolve_route, NavigationTarget, RouteResolution,
};
use crate::routing::utils::{extract_lang_and_path, get_localized_route};

/// Navigation state for managing URL changes without page reloads
#[derive(Clone, Debug)]
pub struct NavigationContext {
    /// Current route path
    pub current_route: RwSignal<String>,
    /// Current language
    pub current_language: RwSignal<String>,
    /// Navigation history for back/forward functionality
    pub history: RwSignal<Vec<String>>,
    /// Current position in history
    pub history_position: RwSignal<usize>,
    /// Whether navigation is in progress
    pub navigating: RwSignal<bool>,
    /// Route metadata cache for performance
    pub route_cache: RwSignal<HashMap<String, RouteResolution>>,
}

/// Navigation event data
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NavigationEvent {
    pub from_route: String,
    pub to_route: String,
    pub language: String,
    pub timestamp: u64,
    pub navigation_type: NavigationType,
}

/// Type of navigation that occurred
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NavigationType {
    /// Direct navigation (clicked link, typed URL)
    Direct,
    /// Language switch
    LanguageSwitch,
    /// Back button
    Back,
    /// Forward button
    Forward,
    /// Programmatic navigation
    Programmatic,
}

impl NavigationContext {
    /// Create new navigation context with existing signals (for use within
    /// reactive context)
    pub fn new_with_signals(
        current_route: RwSignal<String>,
        current_language: RwSignal<String>,
        history: RwSignal<Vec<String>>,
        history_position: RwSignal<usize>,
        navigating: RwSignal<bool>,
        route_cache: RwSignal<HashMap<String, RouteResolution>>,
    ) -> Self {
        // Initialize with current browser URL if in browser
        #[cfg(target_arch = "wasm32")]
        {
            if let Some(initial_path) = Self::get_current_browser_path() {
                let (lang, _clean_path) = extract_lang_and_path(&initial_path);
                current_route.set(initial_path);
                current_language.set(lang);
            }
        }

        Self {
            current_route,
            current_language,
            history,
            history_position,
            navigating,
            route_cache,
        }
    }

    /// Navigate to a new route without page reload
    pub fn navigate_to(&self, path: &str, navigation_type: NavigationType) -> bool {
        // Prevent navigation if already in progress
        if self.navigating.get() {
            tracing::warn!("Navigation already in progress, ignoring: {}", path);
            return false;
        }

        tracing::info!("Navigating to: {} (type: {:?})", path, navigation_type);

        // Mark navigation as in progress
        self.navigating.set(true);

        let current_route = self.current_route.get();
        let (new_lang, _clean_path) = extract_lang_and_path(path);

        // Validate route exists using routing engine
        let route_resolution = self.resolve_route_cached(path);

        #[cfg(target_arch = "wasm32")]
        web_sys::console::log_1(
            &format!(
                "🔍 NAVIGATION: Route resolution for {}: exists={}, component={:?}",
                path, route_resolution.exists, route_resolution.component
            )
            .into(),
        );

        // TEMPORARY: Skip validation completely for debugging language switching
        if !route_resolution.exists {
            #[cfg(target_arch = "wasm32")]
            web_sys::console::log_1(
                &format!(
                    "⚠️ NAVIGATION: Route {} doesn't exist in engine, but allowing for debugging",
                    path
                )
                .into(),
            );
        }

        // Handle external URLs would be checked here if needed

        // Update browser URL first
        if !self.update_browser_url(path) {
            tracing::error!("Failed to update browser URL");
            self.navigating.set(false);
            return false;
        }

        // Update navigation state
        self.update_navigation_state(path, &new_lang, navigation_type.clone());

        // Update document metadata if needed
        self.update_document_metadata(&route_resolution);

        // Create navigation event
        let nav_event = NavigationEvent {
            from_route: current_route,
            to_route: path.to_string(),
            language: new_lang.clone(),
            timestamp: Self::current_timestamp(),
            navigation_type,
        };

        // Dispatch navigation event for listeners
        self.dispatch_navigation_event(&nav_event);

        // Mark navigation as complete
        self.navigating.set(false);

        tracing::info!("Navigation completed successfully to: {}", path);
        true
    }

    /// Navigate using a NavigationTarget
    pub fn navigate_to_target(&self, target: &NavigationTarget) -> bool {
        match target {
            NavigationTarget::Route(path) => self.navigate_to(path, NavigationType::Direct),
            NavigationTarget::External(url) => self.handle_external_url(url),
            NavigationTarget::Email(email) | NavigationTarget::Mailto(email) => {
                self.handle_mailto(email)
            }
            NavigationTarget::Phone(phone) | NavigationTarget::Tel(phone) => self.handle_tel(phone),
        }
    }

    /// Handle external URL navigation
    fn handle_external_navigation(&self, _path: &str) -> bool {
        // External URLs should not be handled by internal navigation
        tracing::warn!("Attempted to navigate to external URL internally");
        false
    }

    /// Handle external URL by opening in new tab/window
    fn handle_external_url(&self, url: &str) -> bool {
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::window;
            if let Some(window) = window() {
                if let Err(e) = window.open_with_url_and_target(url, "_blank") {
                    tracing::error!("Failed to open external URL: {:?}", e);
                    return false;
                }
                return true;
            }
        }
        tracing::debug!("Would open external URL: {}", url);
        false
    }

    /// Handle mailto navigation
    fn handle_mailto(&self, email: &str) -> bool {
        let mailto_url = format!("mailto:{email}");
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::window;
            if let Some(window) = window() {
                let location = window.location();
                if location.set_href(&mailto_url).is_ok() {
                    return true;
                }
            }
        }
        tracing::debug!("Would open mailto: {}", mailto_url);
        false
    }

    /// Handle telephone navigation
    fn handle_tel(&self, phone: &str) -> bool {
        let tel_url = format!("tel:{phone}");
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::window;
            if let Some(window) = window() {
                let location = window.location();
                if location.set_href(&tel_url).is_ok() {
                    return true;
                }
            }
        }
        tracing::debug!("Would open tel: {}", tel_url);
        false
    }

    /// Switch language and navigate to equivalent route
    pub fn switch_language(&self, new_lang: &str) -> bool {
        let current_path = self.current_route.get();

        #[cfg(target_arch = "wasm32")]
        web_sys::console::log_1(
            &format!(
                "🔄 NAVIGATION: Switching from {} to language {}",
                current_path, new_lang
            )
            .into(),
        );

        // Get equivalent route in new language using routing engine
        let target_url = get_language_switch_url(&current_path, new_lang);

        #[cfg(target_arch = "wasm32")]
        web_sys::console::log_1(
            &format!("🎯 NAVIGATION: Generated target URL: {}", target_url).into(),
        );

        if !target_url.is_empty() && target_url != current_path {
            #[cfg(target_arch = "wasm32")]
            web_sys::console::log_1(
                &format!(
                    "✅ NAVIGATION: Using generated URL, navigating to: {}",
                    target_url
                )
                .into(),
            );
            return self.navigate_to(&target_url, NavigationType::LanguageSwitch);
        }

        // Fallback to basic localization - use the full current path, not just clean
        // path
        let localized_path = get_localized_route(&current_path, new_lang);

        #[cfg(target_arch = "wasm32")]
        web_sys::console::log_1(
            &format!(
                "⚠️ NAVIGATION: Using fallback localization: {} -> {}",
                current_path, localized_path
            )
            .into(),
        );

        self.navigate_to(&localized_path, NavigationType::LanguageSwitch)
    }

    /// Go back in history
    pub fn go_back(&self) -> bool {
        let current_position = self.history_position.get();
        if current_position > 0 {
            let new_position = current_position - 1;
            let history_items = self.history.get();
            if let Some(target_route) = history_items.get(new_position) {
                self.history_position.set(new_position);
                return self.navigate_to(target_route, NavigationType::Back);
            }
        }
        false
    }

    /// Go forward in history
    pub fn go_forward(&self) -> bool {
        let current_position = self.history_position.get();
        let history_items = self.history.get();
        if current_position + 1 < history_items.len() {
            let new_position = current_position + 1;
            if let Some(target_route) = history_items.get(new_position) {
                self.history_position.set(new_position);
                return self.navigate_to(target_route, NavigationType::Forward);
            }
        }
        false
    }

    /// Update browser URL using History API
    fn update_browser_url(&self, path: &str) -> bool {
        #[cfg(target_arch = "wasm32")]
        {
            use wasm_bindgen::JsValue;
            use web_sys::window;

            if let Some(window) = window() {
                if let Ok(history) = window.history() {
                    match history.push_state_with_url(&JsValue::NULL, "", Some(path)) {
                        Ok(_) => {
                            tracing::debug!("Successfully updated browser URL to: {}", path);
                            return true;
                        }
                        Err(e) => {
                            tracing::error!("Failed to update browser URL: {:?}", e);
                            return false;
                        }
                    }
                } else {
                    tracing::error!("Failed to get browser history");
                    return false;
                }
            } else {
                tracing::error!("Failed to get window object");
                return false;
            }
        }
        #[cfg(not(target_arch = "wasm32"))]
        {
            tracing::debug!("Would update browser URL to: {}", path);
            true // Return true for SSR since we can't actually update URL
        }
    }

    /// Update internal navigation state
    fn update_navigation_state(&self, path: &str, language: &str, navigation_type: NavigationType) {
        // Update current route and language
        self.current_route.set(path.to_string());
        self.current_language.set(language.to_string());

        // Update history for non-back/forward navigations
        match navigation_type {
            NavigationType::Back | NavigationType::Forward => {
                // History position already updated in go_back/go_forward
            }
            _ => {
                // Add to history and update position
                let mut history_items = self.history.get();
                let current_position = self.history_position.get();

                // Remove any forward history when navigating to new route
                if current_position + 1 < history_items.len() {
                    history_items.truncate(current_position + 1);
                }

                // Add new route to history
                history_items.push(path.to_string());
                let new_position = history_items.len() - 1;

                self.history.set(history_items);
                self.history_position.set(new_position);
            }
        }
    }

    /// Update document metadata based on route resolution
    fn update_document_metadata(&self, _route_resolution: &RouteResolution) {
        #[cfg(target_arch = "wasm32")]
        {
            use wasm_bindgen::JsCast;
            use web_sys::window;

            if let Some(window) = window() {
                if let Some(document) = window.document() {
                    // Update document title with translation
                    if let Some(title_key) = &_route_resolution.metadata.title {
                        // For client-side navigation, translate the title key
                        if let Some(i18n) = use_context::<UnifiedI18n>() {
                            let translated_title = i18n.t(title_key);
                            document.set_title(&translated_title);
                        } else {
                            // For SSR or when context is unavailable, use the raw key as fallback
                            // This shouldn't normally happen since this is client-side only code
                            document.set_title(title_key);
                        }
                    }

                    // Update meta description
                    if let Some(description) = &_route_resolution.metadata.description {
                        if let Ok(Some(meta_tag)) =
                            document.query_selector("meta[name='description']")
                        {
                            if let Ok(meta_element) =
                                meta_tag.dyn_into::<web_sys::HtmlMetaElement>()
                            {
                                meta_element.set_content(description);
                            }
                        }
                    }

                    // Update lang attribute
                    if let Some(html_element) = document.document_element() {
                        let current_lang = self.current_language.get();
                        let _ = html_element.set_attribute("lang", &current_lang);
                    }
                }
            }
        }
    }

    /// Resolve route with caching
    fn resolve_route_cached(&self, path: &str) -> RouteResolution {
        let mut cache = self.route_cache.get();

        // Check cache first
        if let Some(cached_resolution) = cache.get(path) {
            return cached_resolution.clone();
        }

        // Resolve using routing engine
        let resolution = resolve_route(path);

        // Cache the result
        cache.insert(path.to_string(), resolution.clone());
        self.route_cache.set(cache);

        resolution
    }

    /// Dispatch navigation event for listeners
    fn dispatch_navigation_event(&self, _event: &NavigationEvent) {
        // Could dispatch custom events here for other components to listen to
        #[cfg(target_arch = "wasm32")]
        {
            use wasm_bindgen::JsValue;
            use web_sys::{window, CustomEvent};

            if let Some(window) = window() {
                if let Ok(custom_event) = CustomEvent::new("navigation") {
                    let _ = window.dispatch_event(&custom_event);
                }
            }
        }
    }

    /// Get current browser path
    #[cfg(target_arch = "wasm32")]
    fn get_current_browser_path() -> Option<String> {
        use web_sys::window;

        if let Some(window) = window() {
            if let Ok(location) = window.location().pathname() {
                return Some(location);
            }
        }
        None
    }

    /// Get current timestamp in milliseconds
    fn current_timestamp() -> u64 {
        #[cfg(target_arch = "wasm32")]
        {
            use web_sys::js_sys::Date;
            Date::now() as u64
        }
        #[cfg(not(target_arch = "wasm32"))]
        {
            use std::time::{SystemTime, UNIX_EPOCH};
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_millis() as u64
        }
    }

    /// Get current route
    pub fn get_current_route(&self) -> String {
        self.current_route.get()
    }

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

    /// Check if navigation is in progress
    pub fn is_navigating(&self) -> bool {
        self.navigating.get()
    }

    /// Check if can go back
    pub fn can_go_back(&self) -> bool {
        self.history_position.get() > 0
    }

    /// Check if can go forward
    pub fn can_go_forward(&self) -> bool {
        let position = self.history_position.get();
        let history_len = self.history.get().len();
        position + 1 < history_len
    }

    /// Clear route cache
    pub fn clear_route_cache(&self) {
        self.route_cache.set(HashMap::new());
    }
}

// Note: Default implementation removed - NavigationContext must be created
// within a reactive context using NavigationProvider

/// Navigation provider component
#[component]
pub fn NavigationProvider(children: Children) -> impl IntoView {
    // Create reactive signals within the component's reactive context
    let current_route = RwSignal::new("/".to_string());

    // Use dynamic default language instead of hardcoded "en"
    let language_registry = crate::i18n::language_config::get_language_registry();
    let default_language = language_registry.get_default_language().to_string();
    let current_language = RwSignal::new(default_language);

    let history = RwSignal::new(vec!["/".to_string()]);
    let history_position = RwSignal::new(0);
    let navigating = RwSignal::new(false);
    let route_cache = RwSignal::new(HashMap::new());

    // Create navigation context with the signals
    let navigation_context = NavigationContext::new_with_signals(
        current_route,
        current_language,
        history,
        history_position,
        navigating,
        route_cache,
    );

    provide_context(navigation_context);
    children()
}

/// Hook to use navigation context
pub fn use_navigation() -> NavigationContext {
    expect_context::<NavigationContext>()
}

/// Hook to get current route as a signal
pub fn use_current_route() -> ReadSignal<String> {
    let context = use_navigation();
    context.current_route.read_only()
}

/// Hook to navigate programmatically
pub fn use_navigate() -> impl Fn(&str) -> bool + Clone {
    let context = use_navigation();
    move |path: &str| context.navigate_to(path, NavigationType::Programmatic)
}

/// Hook to navigate using NavigationTarget
pub fn use_navigate_target() -> impl Fn(&NavigationTarget) -> bool + Clone {
    let context = use_navigation();
    move |target: &NavigationTarget| context.navigate_to_target(target)
}

/// Hook for language switching with navigation
pub fn use_language_navigate() -> impl Fn(&str) -> bool + Clone {
    let context = use_navigation();
    move |lang: &str| context.switch_language(lang)
}

/// Hook for history navigation
pub fn use_history_navigation() -> (impl Fn() -> bool + Clone, impl Fn() -> bool + Clone) {
    let context = use_navigation();
    let go_back = {
        let ctx = context.clone();
        move || ctx.go_back()
    };
    let go_forward = {
        let ctx = context.clone();
        move || ctx.go_forward()
    };
    (go_back, go_forward)
}

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

    #[test]
    fn test_navigation_context_creation() {
        // Note: NavigationContext requires reactive context - test disabled
        // let context = NavigationContext::new();
        // assert_eq!(context.get_current_route(), "/");
        // assert_eq!(context.get_current_language(), "en");
        // assert!(!context.is_navigating());
        // assert!(!context.can_go_back());
        // assert!(!context.can_go_forward());
    }

    #[test]
    fn test_navigation_type_serialization() {
        let nav_type = NavigationType::LanguageSwitch;
        let serialized = serde_json::to_string(&nav_type).unwrap();
        let deserialized: NavigationType = serde_json::from_str(&serialized).unwrap();

        match (nav_type, deserialized) {
            (NavigationType::LanguageSwitch, NavigationType::LanguageSwitch) => (),
            _ => panic!("Serialization/deserialization failed"),
        }
    }

    #[test]
    fn test_navigation_event_creation() {
        let event = NavigationEvent {
            from_route: "/".to_string(),
            to_route: "/about".to_string(),
            language: "en".to_string(),
            timestamp: 1000,
            navigation_type: NavigationType::Direct,
        };

        assert_eq!(event.from_route, "/");
        assert_eq!(event.to_route, "/about");
        assert_eq!(event.language, "en");
    }
}
