use crate::auth::{use_auth_context, AuthResult, AuthError};
use gloo_net::http::{Request, Response, Headers};
use leptos::prelude::*;
use leptos_router::hooks::use_navigate;
use leptos_router::NavigateOptions;
use serde_json::Value;
use std::collections::HashMap;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::{console, AbortController};

pub struct HttpInterceptor {
    auth_context: crate::auth::token_manager::AuthContext,
    navigate: leptos_router::NavigateOptions,
}

impl HttpInterceptor {
    pub fn new(
        auth_context: crate::auth::token_manager::AuthContext,
        navigate: leptos_router::NavigateOptions,
    ) -> Self {
        Self {
            auth_context,
            navigate,
        }
    }

    pub async fn request(&self, mut request: Request) -> AuthResult<Request> {
        // Add authentication header if available
        if let Ok(Some(auth_header)) = self.auth_context.token_manager.get_auth_header() {
            request = request.header("Authorization", &auth_header);
        }

        // Add common headers
        request = request
            .header("Content-Type", "application/json")
            .header("Accept", "application/json")
            .header("X-Requested-With", "XMLHttpRequest");

        Ok(request)
    }

    pub async fn response(&self, response: Response) -> AuthResult<Response> {
        match response.status() {
            401 => {
                // Unauthorized - token might be expired or invalid
                self.handle_401_response().await?;
                Err(AuthError {
                    message: "Authentication required".to_string(),
                    code: Some("UNAUTHORIZED".to_string()),
                    field: None,
                })
            }
            403 => {
                // Forbidden - user doesn't have permission
                console::warn_1(&"Access denied - insufficient permissions".into());
                Ok(response)
            }
            429 => {
                // Too Many Requests - rate limited
                console::warn_1(&"Rate limit exceeded".into());
                Ok(response)
            }
            500..=599 => {
                // Server errors
                console::error_1(&format!("Server error: {}", response.status()).into());
                Ok(response)
            }
            _ => Ok(response),
        }
    }

    async fn handle_401_response(&self) -> AuthResult<()> {
        console::log_1(&"Handling 401 response - attempting token refresh".into());

        // Try to refresh the token
        match self.auth_context.token_manager.refresh_if_needed().await {
            Ok(Some(_new_token)) => {
                console::log_1(&"Token refreshed successfully".into());
                Ok(())
            }
            Ok(None) => {
                console::log_1(&"No token to refresh - redirecting to login".into());
                self.logout_and_redirect().await;
                Err(AuthError {
                    message: "No authentication token available".to_string(),
                    code: Some("NO_TOKEN".to_string()),
                    field: None,
                })
            }
            Err(e) => {
                console::error_1(&format!("Token refresh failed: {}", e).into());
                self.logout_and_redirect().await;
                Err(e)
            }
        }
    }

    async fn logout_and_redirect(&self) {
        // Clear authentication state
        let _ = self.auth_context.token_manager.clear_token();
        self.auth_context.auth_state.update(|state| {
            *state = crate::auth::AuthState::default();
        });

        // Redirect to login page
        self.navigate("/login", Default::default());
    }
}

// Global HTTP interceptor setup
pub fn setup_http_interceptor() {
    let auth_context = use_auth_context();
    let navigate = use_navigate();

    let interceptor = HttpInterceptor::new(auth_context, navigate);

    // Store interceptor in global context for use with HTTP requests
    provide_context(interceptor);
}

pub fn use_http_interceptor() -> HttpInterceptor {
    expect_context::<HttpInterceptor>()
}

// Enhanced HTTP client with automatic token refresh and error handling
pub struct AuthenticatedHttpClient {
    interceptor: HttpInterceptor,
    base_url: String,
    default_headers: HashMap<String, String>,
}

impl AuthenticatedHttpClient {
    pub fn new(
        auth_context: crate::auth::token_manager::AuthContext,
        navigate: leptos_router::NavigateOptions,
        base_url: String,
    ) -> Self {
        let interceptor = HttpInterceptor::new(auth_context, navigate);
        let mut default_headers = HashMap::new();
        default_headers.insert("Content-Type".to_string(), "application/json".to_string());
        default_headers.insert("Accept".to_string(), "application/json".to_string());

        Self {
            interceptor,
            base_url,
            default_headers,
        }
    }

    pub async fn get(&self, path: &str) -> AuthResult<Response> {
        let url = format!("{}{}", self.base_url, path);
        let mut request = Request::get(&url);

        // Add default headers
        for (key, value) in &self.default_headers {
            request = request.header(key, value);
        }

        self.execute_request(request).await
    }

    pub async fn post<T: serde::Serialize>(&self, path: &str, body: &T) -> AuthResult<Response> {
        let url = format!("{}{}", self.base_url, path);
        let mut request = Request::post(&url);

        // Add default headers
        for (key, value) in &self.default_headers {
            request = request.header(key, value);
        }

        let request = request.json(body).map_err(|e| AuthError {
            message: format!("Failed to serialize request body: {}", e),
            code: Some("SERIALIZATION_ERROR".to_string()),
            field: None,
        })?;

        self.execute_request(request).await
    }

    pub async fn put<T: serde::Serialize>(&self, path: &str, body: &T) -> AuthResult<Response> {
        let url = format!("{}{}", self.base_url, path);
        let mut request = Request::put(&url);

        // Add default headers
        for (key, value) in &self.default_headers {
            request = request.header(key, value);
        }

        let request = request.json(body).map_err(|e| AuthError {
            message: format!("Failed to serialize request body: {}", e),
            code: Some("SERIALIZATION_ERROR".to_string()),
            field: None,
        })?;

        self.execute_request(request).await
    }

    pub async fn delete(&self, path: &str) -> AuthResult<Response> {
        let url = format!("{}{}", self.base_url, path);
        let mut request = Request::delete(&url);

        // Add default headers
        for (key, value) in &self.default_headers {
            request = request.header(key, value);
        }

        self.execute_request(request).await
    }

    pub async fn patch<T: serde::Serialize>(&self, path: &str, body: &T) -> AuthResult<Response> {
        let url = format!("{}{}", self.base_url, path);
        let mut request = Request::patch(&url);

        // Add default headers
        for (key, value) in &self.default_headers {
            request = request.header(key, value);
        }

        let request = request.json(body).map_err(|e| AuthError {
            message: format!("Failed to serialize request body: {}", e),
            code: Some("SERIALIZATION_ERROR".to_string()),
            field: None,
        })?;

        self.execute_request(request).await
    }

    async fn execute_request(&self, request: Request) -> AuthResult<Response> {
        // Apply request interceptor
        let request = self.interceptor.request(request).await?;

        // Execute request with retry logic for 401 responses
        let mut retries = 0;
        const MAX_RETRIES: u32 = 1;

        loop {
            let response = request.send().await.map_err(|e| AuthError {
                message: format!("HTTP request failed: {:?}", e),
                code: Some("REQUEST_FAILED".to_string()),
                field: None,
            })?;

            // Check if this is a 401 and we haven't exceeded retry limit
            if response.status() == 401 && retries < MAX_RETRIES {
                console::log_1(&"Received 401, attempting token refresh and retry".into());

                // Try to refresh token
                match self.interceptor.auth_context.token_manager.refresh_if_needed().await {
                    Ok(Some(_)) => {
                        console::log_1(&"Token refreshed, retrying request".into());
                        retries += 1;
                        continue;
                    }
                    _ => {
                        console::log_1(&"Token refresh failed, handling 401".into());
                        return self.interceptor.response(response).await;
                    }
                }
            }

            // Apply response interceptor
            return self.interceptor.response(response).await;
        }
    }
}

// Request timeout handler
pub struct RequestTimeoutHandler {
    timeout_ms: u32,
}

impl RequestTimeoutHandler {
    pub fn new(timeout_ms: u32) -> Self {
        Self { timeout_ms }
    }

    pub async fn execute_with_timeout<F, T>(&self, future: F) -> AuthResult<T>
    where
        F: std::future::Future<Output = AuthResult<T>>,
    {
        let timeout_future = self.create_timeout();

        match futures::future::select(
            Box::pin(future),
            Box::pin(timeout_future)
        ).await {
            futures::future::Either::Left((result, _)) => result,
            futures::future::Either::Right((_, _)) => Err(AuthError {
                message: format!("Request timeout after {}ms", self.timeout_ms),
                code: Some("REQUEST_TIMEOUT".to_string()),
                field: None,
            }),
        }
    }

    async fn create_timeout(&self) -> AuthResult<()> {
        let promise = js_sys::Promise::new(&mut |resolve, _reject| {
            let timeout = gloo_timers::callback::Timeout::new(self.timeout_ms, move || {
                resolve.call0(&JsValue::UNDEFINED).unwrap();
            });
            timeout.forget();
        });

        JsFuture::from(promise).await.map_err(|_| AuthError {
            message: "Timeout promise failed".to_string(),
            code: Some("TIMEOUT_PROMISE_FAILED".to_string()),
            field: None,
        })?;

        Ok(())
    }
}

// Error response parser
pub fn parse_error_response(response: &Response) -> Option<AuthError> {
    // Try to parse error from response body
    // This is a simplified version - in practice you'd handle this asynchronously
    None
}

// Network status monitor
pub struct NetworkStatusMonitor {
    is_online: RwSignal<bool>,
}

impl NetworkStatusMonitor {
    pub fn new() -> Self {
        let is_online = RwSignal::new(true);

        // Monitor online/offline events
        if let Some(window) = web_sys::window() {
            let online_closure = {
                let is_online = is_online.clone();
                Closure::wrap(Box::new(move |_: web_sys::Event| {
                    is_online.set(true);
                    console::log_1(&"Network connection restored".into());
                }) as Box<dyn Fn(web_sys::Event)>)
            };

            let offline_closure = {
                let is_online = is_online.clone();
                Closure::wrap(Box::new(move |_: web_sys::Event| {
                    is_online.set(false);
                    console::log_1(&"Network connection lost".into());
                }) as Box<dyn Fn(web_sys::Event)>)
            };

            let _ = window.add_event_listener_with_callback("online", online_closure.as_ref().unchecked_ref());
            let _ = window.add_event_listener_with_callback("offline", offline_closure.as_ref().unchecked_ref());

            online_closure.forget();
            offline_closure.forget();
        }

        Self { is_online }
    }

    pub fn is_online(&self) -> bool {
        self.is_online.get()
    }

    pub fn on_network_change<F>(&self, callback: F)
    where
        F: Fn(bool) + 'static,
    {
        Effect::new({
            let is_online = self.is_online;
            move |_| {
                callback(is_online.get());
            }
        });
    }
}

// Request queue for offline support
pub struct OfflineRequestQueue {
    queue: RwSignal<Vec<QueuedRequest>>,
    network_monitor: NetworkStatusMonitor,
}

#[derive(Clone, Debug)]
pub struct QueuedRequest {
    pub method: String,
    pub url: String,
    pub headers: HashMap<String, String>,
    pub body: Option<String>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

impl OfflineRequestQueue {
    pub fn new() -> Self {
        let queue = RwSignal::new(Vec::new());
        let network_monitor = NetworkStatusMonitor::new();

        // Process queue when network comes back online
        network_monitor.on_network_change({
            let queue = queue.clone();
            move |is_online| {
                if is_online {
                    // Process queued requests
                    let queued_requests = queue.get();
                    for request in queued_requests {
                        // TODO: Implement request replay logic
                        console::log_1(&format!("Replaying request: {} {}", request.method, request.url).into());
                    }
                    queue.set(Vec::new());
                }
            }
        });

        Self {
            queue,
            network_monitor,
        }
    }

    pub fn enqueue_request(&self, request: QueuedRequest) {
        self.queue.update(|queue| {
            queue.push(request);
        });
    }

    pub fn is_online(&self) -> bool {
        self.network_monitor.is_online()
    }

    pub fn queue_size(&self) -> usize {
        self.queue.get().len()
    }
}
