//! WASM fetch utilities with centralized 401 interception.
//!
//! Pattern: the framework layer knows about HTTP 401, nothing more.
//! The app layer registers a handler that owns `AuthState` + `set_path`.
//!
//! # Usage
//!
//! At app startup (WASM only):
//! ```ignore
//! rustelo_core_lib::utils::fetch::register_unauthorized_handler(move || {
//!     auth_state.clear_auth();
//!     let path = current_path_from_location();
//!     set_path.set(format!("/login?return={}", path));
//! });
//! ```
//!
//! In any async fetch:
//! ```ignore
//! let resp = guarded_get("/api/some-resource", "application/json").await?;
//! ```

#[cfg(target_arch = "wasm32")]
use std::cell::RefCell;

/// HTTP 401 status code.
const HTTP_UNAUTHORIZED: u16 = 401;

#[cfg(target_arch = "wasm32")]
thread_local! {
    static UNAUTHORIZED_HANDLER: RefCell<Option<Box<dyn Fn()>>> = const { RefCell::new(None) };
}

/// Register a handler that fires whenever any guarded fetch receives HTTP 401.
///
/// Call once at WASM startup from the app root, after `AuthState` and
/// `set_path` are in scope. Replaces any previously registered handler.
#[cfg(target_arch = "wasm32")]
pub fn register_unauthorized_handler(handler: impl Fn() + 'static) {
    UNAUTHORIZED_HANDLER.with(|h| {
        *h.borrow_mut() = Some(Box::new(handler));
    });
}

/// Dispatch the registered 401 handler, if any.
///
/// Called internally by fetch wrappers on HTTP 401. Safe to call even if no
/// handler is registered (no-op in that case).
#[cfg(target_arch = "wasm32")]
pub fn on_unauthorized() {
    UNAUTHORIZED_HANDLER.with(|h| {
        if let Some(handler) = h.borrow().as_ref() {
            handler();
        }
    });
}

/// Perform a guarded GET request via `gloo_net`.
///
/// On HTTP 401: fires the registered unauthorized handler and returns `Err`.
/// On other non-2xx: returns `Err` with the status code.
/// On network error: returns `Err` with the error string.
#[cfg(target_arch = "wasm32")]
pub async fn guarded_get(url: &str, accept: &str) -> Result<gloo_net::http::Response, String> {
    let resp = gloo_net::http::Request::get(url)
        .header("Accept", accept)
        .send()
        .await
        .map_err(|e| e.to_string())?;

    if resp.status() == HTTP_UNAUTHORIZED {
        on_unauthorized();
        return Err(format!("401 Unauthorized: {url}"));
    }

    if !resp.ok() {
        return Err(format!("HTTP {}: {url}", resp.status()));
    }

    Ok(resp)
}

/// Return the current browser path, suitable for a `?return=` redirect
/// parameter.
///
/// Returns `"/"` if the Location API is unavailable.
#[cfg(target_arch = "wasm32")]
pub fn current_location_path() -> String {
    web_sys::window()
        .and_then(|w| w.location().pathname().ok())
        .unwrap_or_else(|| "/".to_string())
}
