use leptos::ev::MouseEvent;
#[cfg(target_arch = "wasm32")]
use leptos::prelude::Effect;
use leptos::prelude::{Set, WriteSignal};
use serde::{Deserialize, Serialize};
use std::rc::Rc;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
#[cfg(target_arch = "wasm32")]
use web_sys::window;
#[cfg(not(target_arch = "wasm32"))]
fn window() -> Option<()> {
    None
}

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

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

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

/// Creates a navigation function for SPA routing.
#[cfg(target_arch = "wasm32")]
pub fn make_navigate(set_path: WriteSignal<String>) -> NavigateFn {
    Rc::new(move |to: &str| {
        web_sys::console::log_1(&format!("Navigating to: {to}").into());
        if let Some(win) = window() {
            if let Some(history) = win.history().ok() {
                let _ = history.push_state_with_url(&wasm_bindgen::JsValue::NULL, "", Some(to));
            }
        }
        set_path.set(to.to_string());
    })
}

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

/// Generic API request function for making HTTP requests to the server
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.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 {
    if window().is_some() {
        Rc::new(move |ev: MouseEvent, to: &str| {
            web_sys::console::log_1(&format!("Clicked: {to}").into());
            ev.prevent_default();
            set_path.set(to.to_string());
            (*navigate)(to);
        })
    } else {
        Rc::new(|_, _| {})
    }
}

/// 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>) {}
