use std::sync::Arc;

use leptos::ev::MouseEvent;
#[cfg(target_arch = "wasm32")]
use leptos::prelude::set_timeout;
#[cfg(target_arch = "wasm32")]
use leptos::prelude::Effect;
use leptos::prelude::{Set, WriteSignal};
use serde::{Deserialize, Serialize};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
#[cfg(target_arch = "wasm32")]
use web_sys::window;

// Note: extract_lang_and_path implementation moved to routing::utils module

/// Console logging control with multiple activation methods:
/// 1. Build-time: cargo build --features console-log (dev) or
///    console-log-production (prod)
/// 2. Runtime: localStorage.setItem('rustelo_debug', 'true')
/// 3. Runtime: URL parameter ?debug=true or ?console=true
/// 4. Runtime: window.RUSTELO_DEBUG = true
#[cfg(target_arch = "wasm32")]
pub fn is_console_logging_enabled() -> bool {
    // Always enabled if build feature is active
    #[cfg(any(feature = "console-log", feature = "console-log-production"))]
    {
        return true;
    }

    // Runtime checks for production debugging
    if let Some(window) = web_sys::window() {
        // Check URL parameters
        let location = window.location();
        {
            if let Ok(search) = location.search() {
                if search.contains("debug=true")
                    || search.contains("console=true")
                    || search.contains("log=true")
                {
                    return true;
                }
            }
        }

        // Check localStorage
        if let Ok(Some(storage)) = window.local_storage() {
            if let Ok(Some(debug)) = storage.get_item("rustelo_debug") {
                if debug == "true" || debug == "1" || debug == "on" {
                    return true;
                }
            }
            if let Ok(Some(console)) = storage.get_item("rustelo_console") {
                if console == "true" || console == "1" || console == "on" {
                    return true;
                }
            }
        }

        // Check global window property
        if let Ok(js_val) = js_sys::Reflect::get(&window, &"RUSTELO_DEBUG".into()) {
            if js_val.is_truthy() {
                return true;
            }
        }
    }

    false
}

/// SSR-safe version that always returns false
#[cfg(not(target_arch = "wasm32"))]
pub fn is_console_logging_enabled() -> bool {
    false
}

/// Runtime console logging control functions for production debugging
#[cfg(target_arch = "wasm32")]
pub mod console_control {
    /// Enable console logging via localStorage (persists across page reloads)
    pub fn enable_console_logging() {
        if let Some(window) = web_sys::window() {
            if let Ok(Some(storage)) = window.local_storage() {
                let _ = storage.set_item("rustelo_debug", "true");
            }
        }
    }

    /// Disable console logging via localStorage
    pub fn disable_console_logging() {
        if let Some(window) = web_sys::window() {
            if let Ok(Some(storage)) = window.local_storage() {
                let _ = storage.remove_item("rustelo_debug");
                let _ = storage.remove_item("rustelo_console");
            }
        }
    }

    /// Enable console logging via global window property (session-based)
    pub fn enable_session_logging() {
        if let Some(window) = web_sys::window() {
            let _ = js_sys::Reflect::set(&window, &"RUSTELO_DEBUG".into(), &true.into());
        }
    }

    /// Disable console logging via global window property
    pub fn disable_session_logging() {
        if let Some(window) = web_sys::window() {
            let _ = js_sys::Reflect::set(&window, &"RUSTELO_DEBUG".into(), &false.into());
        }
    }

    /// Check current logging status
    pub fn is_logging_enabled() -> bool {
        super::is_console_logging_enabled()
    }

    /// Get logging status info for debugging
    pub fn get_logging_info() -> String {
        let mut info = Vec::new();

        #[cfg(feature = "console-log")]
        info.push("✅ console-log feature enabled");

        #[cfg(feature = "console-log-production")]
        info.push("✅ console-log-production feature enabled");

        if let Some(window) = web_sys::window() {
            // Check URL params
            let location = window.location();
            {
                if let Ok(search) = location.search() {
                    if search.contains("debug=true") {
                        info.push("✅ URL parameter debug=true");
                    }
                    if search.contains("console=true") {
                        info.push("✅ URL parameter console=true");
                    }
                    if search.contains("log=true") {
                        info.push("✅ URL parameter log=true");
                    }
                }
            }

            // Check localStorage
            if let Ok(Some(storage)) = window.local_storage() {
                if let Ok(Some(debug)) = storage.get_item("rustelo_debug") {
                    if debug == "true" {
                        info.push("✅ localStorage rustelo_debug=true");
                    }
                }
                if let Ok(Some(console)) = storage.get_item("rustelo_console") {
                    if console == "true" {
                        info.push("✅ localStorage rustelo_console=true");
                    }
                }
            }

            // Check global
            if let Ok(js_val) = js_sys::Reflect::get(&window, &"RUSTELO_DEBUG".into()) {
                if js_val.is_truthy() {
                    info.push("✅ window.RUSTELO_DEBUG=true");
                }
            }
        }

        if info.is_empty() {
            "🔇 Console logging disabled".to_string()
        } else {
            format!("🔊 Console logging enabled via: {}", info.join(", "))
        }
    }
}

/// SSR-safe console control module
#[cfg(not(target_arch = "wasm32"))]
pub mod console_control {
    pub fn enable_console_logging() {}
    pub fn disable_console_logging() {}
    pub fn enable_session_logging() {}
    pub fn disable_session_logging() {}
    pub fn is_logging_enabled() -> bool {
        false
    }
    pub fn get_logging_info() -> String {
        "SSR: Console logging not available".to_string()
    }
}

/// Safe console logging with dynamic control
/// Can be activated via:
/// - Build features: console-log, console-log-production
/// - URL params: ?debug=true, ?console=true, ?log=true
/// - localStorage: rustelo_debug=true, rustelo_console=true
/// - Global: window.RUSTELO_DEBUG=true
#[allow(unused_macros)]
#[macro_export]
macro_rules! safe_console_log {
    ($msg:expr) => {
        #[cfg(target_arch = "wasm32")]
        if let Ok(_) = std::panic::catch_unwind(|| {
            if $crate::utils::nav::is_console_logging_enabled() {
                web_sys::console::log_1(&$msg.into());
            }
        }) {
            // Console log succeeded or was disabled
        }
    };
}

/// Safe console error logging with dynamic control
/// Always shows errors if any logging method is enabled
#[allow(unused_macros)]
#[macro_export]
macro_rules! safe_console_error {
    ($msg:expr) => {
        #[cfg(target_arch = "wasm32")]
        if let Ok(_) = std::panic::catch_unwind(|| {
            if $crate::utils::nav::is_console_logging_enabled() {
                web_sys::console::error_1(&$msg.into());
            }
        }) {
            // Console error succeeded or was disabled
        }
    };
}

/// Safe console warning logging with dynamic control
#[allow(unused_macros)]
#[macro_export]
macro_rules! safe_console_warn {
    ($msg:expr) => {
        #[cfg(target_arch = "wasm32")]
        if let Ok(_) = std::panic::catch_unwind(|| {
            if $crate::utils::nav::is_console_logging_enabled() {
                web_sys::console::warn_1(&$msg.into());
            }
        }) {
            // Console warn succeeded or was disabled
        }
    };
}

// --- Type Aliases for Closures ---
pub type NavigateFn = Arc<dyn Fn(&str) + Send + Sync>;
pub type LinkClickFn = Arc<dyn Fn(MouseEvent, &str) + Send + Sync>;

// --- Navigation Context ---
#[derive(Clone)]
pub struct NavigationContext {
    pub navigate: NavigateFn,
    pub on_link_click: LinkClickFn,
}

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

// Returns the initial path for SSR or client hydration, including hash
// fragments.
/// In the future, this could use a context or prop for SSR path awareness.
#[cfg(target_arch = "wasm32")]
pub fn get_initial_path() -> String {
    if let Some(win) = window() {
        let location = win.location();
        let pathname = location.pathname().unwrap_or_else(|_| "/".to_string());
        let hash = location.hash().unwrap_or_else(|_| "".to_string());

        // Combine pathname and hash if hash exists
        if !hash.is_empty() {
            format!("{}{}", pathname, hash)
        } else {
            pathname
        }
    } else {
        "/".to_string()
    }
}

#[cfg(not(target_arch = "wasm32"))]
pub fn get_initial_path() -> String {
    "/".to_string()
}

/// Creates a navigation function for SPA routing with hash fragment support.
#[cfg(target_arch = "wasm32")]
pub fn make_navigate(set_path: WriteSignal<String>) -> NavigateFn {
    Arc::new(move |to: &str| {
        safe_console_log!(format!("Navigating to: {to}"));

        // Split path and hash fragment
        let (path_part, hash_part) = if let Some(hash_index) = to.find('#') {
            (&to[..hash_index], Some(&to[hash_index + 1..]))
        } else {
            (to, None)
        };

        if let Some(win) = window() {
            // Update browser URL with full path including hash
            if let Some(history) = win.history().ok() {
                let _ = history.push_state_with_url(&wasm_bindgen::JsValue::NULL, "", Some(to));
            }

            // If there's a hash fragment, scroll to it after navigation
            if let Some(hash) = hash_part {
                // Use setTimeout to ensure the page has rendered before scrolling
                let hash_to_scroll = hash.to_string();
                set_timeout(
                    move || {
                        if let Some(document) = window().and_then(|w| w.document()) {
                            // First try to find by direct ID
                            if let Some(element) = document.get_element_by_id(&hash_to_scroll) {
                                element.scroll_into_view_with_bool(true);
                                safe_console_log!(format!("Scrolled to hash: #{}", hash_to_scroll));
                                return;
                            }

                            // If not found, try to find by data-anchor-es attribute
                            let selector = format!("[data-anchor-es='{}']", hash_to_scroll);
                            if let Ok(Some(element)) = document.query_selector(&selector) {
                                element.scroll_into_view_with_bool(true);
                                safe_console_log!(format!(
                                    "Scrolled to Spanish anchor: #{}",
                                    hash_to_scroll
                                ));
                                return;
                            }

                            safe_console_log!(format!(
                                "Element with id '{}' or data-anchor-es='{}' not found",
                                hash_to_scroll, hash_to_scroll
                            ));
                        }
                    },
                    std::time::Duration::from_millis(100),
                );
            }
        }

        // Set path without hash for routing (SPA router handles the base path)
        set_path.set(path_part.to_string());
    })
}

#[cfg(not(target_arch = "wasm32"))]
pub fn make_navigate(set_path: WriteSignal<String>) -> NavigateFn {
    Arc::new(move |to: &str| {
        set_path.set(to.to_string());
    })
}

/// Creates a navigation function for SPA routing with hash fragment support.
#[cfg(target_arch = "wasm32")]
pub fn anchor_navigate(set_path: WriteSignal<String>, to: &str) {
    web_sys::console::log_1(
        &format!("🚀 anchor_navigate: Starting navigation to: '{}'", to).into(),
    );

    // Split path and hash fragment
    let (path_part, hash_part) = if let Some(hash_index) = to.find('#') {
        (&to[..hash_index], Some(&to[hash_index + 1..]))
    } else {
        (to, None)
    };

    web_sys::console::log_1(
        &format!(
            "🚀 anchor_navigate: path_part='{}', hash_part={:?}",
            path_part, hash_part
        )
        .into(),
    );

    if let Some(win) = window() {
        // Update browser URL with full path including hash
        if let Some(history) = win.history().ok() {
            web_sys::console::log_1(
                &format!("🚀 anchor_navigate: Updating browser URL to: '{}'", to).into(),
            );
            let _ = history.push_state_with_url(&wasm_bindgen::JsValue::NULL, "", Some(to));
        }

        // If there's a hash fragment, scroll to it after navigation
        if let Some(hash) = hash_part {
            // Use setTimeout to ensure the page has rendered before scrolling
            let hash_to_scroll = hash.to_string();
            set_timeout(
                move || {
                    if let Some(document) = window().and_then(|w| w.document()) {
                        // First try to find by direct ID
                        if let Some(element) = document.get_element_by_id(&hash_to_scroll) {
                            element.scroll_into_view_with_bool(true);
                            web_sys::console::log_1(
                                &format!("Scrolled to hash: #{}", hash_to_scroll).into(),
                            );
                            return;
                        }

                        // If not found, try to find by data-anchor-es attribute
                        let selector = format!("[data-anchor-es='{}']", hash_to_scroll);
                        if let Ok(Some(element)) = document.query_selector(&selector) {
                            element.scroll_into_view_with_bool(true);
                            web_sys::console::log_1(
                                &format!("Scrolled to Spanish anchor: #{}", hash_to_scroll).into(),
                            );
                            return;
                        }

                        web_sys::console::log_1(
                            &format!(
                                "Element with id '{}' or data-anchor-es='{}' not found",
                                hash_to_scroll, hash_to_scroll
                            )
                            .into(),
                        );
                    }
                },
                std::time::Duration::from_millis(100),
            );
        }
    }

    // Set path without hash for routing (SPA router handles the base path)
    web_sys::console::log_1(
        &format!(
            "🚀 anchor_navigate: Setting path signal to: '{}'",
            path_part
        )
        .into(),
    );
    set_path.set(path_part.to_string());
    web_sys::console::log_1(
        &format!("🚀 anchor_navigate: Navigation complete for: '{}'", to).into(),
    );
}

/// Generic API request function for making HTTP requests to the server
#[cfg(target_arch = "wasm32")]
pub async fn api_request<T, R>(
    url: &str,
    method: &str,
    body: Option<T>,
) -> Result<R, Box<dyn std::error::Error>>
where
    T: Serialize,
    R: for<'de> Deserialize<'de>,
{
    let mut request = reqwasm::http::Request::new(url);
    request = match method {
        "GET" => request.method(reqwasm::http::Method::GET),
        "POST" => request.method(reqwasm::http::Method::POST),
        "PUT" => request.method(reqwasm::http::Method::PUT),
        "DELETE" => request.method(reqwasm::http::Method::DELETE),
        "PATCH" => request.method(reqwasm::http::Method::PATCH),
        _ => request.method(reqwasm::http::Method::GET),
    };
    request = request.header("Content-Type", "application/json");

    // Add auth token if available
    if let Some(window) = web_sys::window() {
        if let Ok(Some(storage)) = window.local_storage() {
            if let Ok(Some(token)) = storage.get_item("auth_token") {
                request = request.header("Authorization", &format!("Bearer {token}"));
            }
        }
    }

    // Add body if provided
    if let Some(body) = body {
        let body_str = serde_json::to_string(&body)?;
        request = request.body(body_str);
    }

    let response = request.send().await?;

    if response.status() == 401 {
        #[cfg(target_arch = "wasm32")]
        super::fetch::on_unauthorized();
        return Err("Unauthorized".into());
    }

    if response.ok() {
        let json_response = response.json::<R>().await?;
        Ok(json_response)
    } else {
        let error_text = response
            .text()
            .await
            .unwrap_or_else(|_| "Unknown error".to_string());
        Err(format!("API request failed: {error_text}").into())
    }
}

/// Creates a link click handler for SPA navigation.
pub fn make_on_link_click(set_path: WriteSignal<String>, navigate: NavigateFn) -> LinkClickFn {
    Arc::new(move |ev: MouseEvent, to: &str| {
        if cfg!(target_arch = "wasm32") {
            safe_console_log!(format!("Clicked: {to}"));
            ev.prevent_default();
        }
        set_path.set(to.to_string());
        (*navigate)(to);
    })
}

/// Sets up a popstate event listener for SPA navigation (client only).
#[cfg(target_arch = "wasm32")]
pub fn make_popstate_effect(set_path: WriteSignal<String>) {
    if let Some(win) = window() {
        Effect::new(move |_| {
            let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move || {
                if let Some(win) = window() {
                    let new_path = win
                        .location()
                        .pathname()
                        .unwrap_or_else(|_| "/".to_string());
                    set_path.set(new_path);
                }
            }) as Box<dyn Fn()>);
            let _ =
                win.add_event_listener_with_callback("popstate", closure.as_ref().unchecked_ref());
            closure.forget();
        });
    }
}

/// No-op for server.
#[cfg(not(target_arch = "wasm32"))]
pub fn make_popstate_effect(_set_path: WriteSignal<String>) {}

/// Helper function to extract language code and clean path from URL
/// Uses shared implementation for consistency
pub fn extract_lang_and_path(url_path: &str) -> (String, String) {
    crate::routing::utils::extract_lang_and_path(url_path)
}

/// Get the language for a route by checking routes.toml configuration
/// Returns the language code if found, or default language as fallback
pub fn get_route_language(path: &str) -> String {
    // Load routes from configuration
    let config = crate::routing::utils::load_routes_config();

    // Check if this exact path exists in any route configuration
    for route in &config.routes {
        if route.enabled && route.path == path {
            return route.language.clone();
        }
    }

    // Fallback: use the shared routing system's language detection
    let (detected_lang, _) = crate::routing::utils::extract_lang_and_path(path);
    detected_lang
}

/// Check if a path belongs to a non-default language using routes.toml
pub fn is_non_default_language_route(path: &str) -> bool {
    let detected_lang = get_route_language(path);
    let language_registry = crate::i18n::language_config::get_language_registry();
    let default_language = language_registry.get_default_language();

    detected_lang != default_language
}

#[cfg(target_arch = "wasm32")]
pub fn doc_scroll_to_top(timeout: u64) {
    set_timeout(
        move || {
            // Try multiple scroll approaches
            if let Some(window) = web_sys::window() {
                // Method 1: Use window.scrollTo (most reliable)
                let _ = window.scroll_to_with_x_and_y(0.0, 0.0);

                // Method 2: Use document.documentElement.scrollTop (fallback)
                if let Some(document) = window.document() {
                    if let Some(element) = document.document_element() {
                        element.set_scroll_top(0);
                    }
                }
            }
        },
        std::time::Duration::from_millis(timeout),
    );
}
