use leptos::prelude::*;
use leptos::task::spawn_local;
use leptos_router::hooks::use_navigate;
use std::time::Duration;
use crate::services::auth_service::AuthService;
use crate::types::auth::AuthTokens;

#[derive(Clone, Debug)]
pub struct AuthContext {
    pub tokens: Signal<Option<AuthTokens>>,
    pub set_tokens: WriteSignal<Option<AuthTokens>>,
    pub is_authenticated: Signal<bool>,
    pub login: Action<(String, String), Result<(), String>>,
    pub logout: Action<(), ()>,
}

pub fn provide_auth_context() -> AuthContext {
    let (tokens, set_tokens) = signal(None::<AuthTokens>);
    let auth_service = AuthService::new();

    // Load tokens from storage on mount
    Effect::new(move |_| {
        if let Ok(Some(stored_tokens)) = auth_service.get_tokens() {
            set_tokens.set(Some(stored_tokens));
        }
    });

    // Derived signal for authentication status
    let is_authenticated = Signal::derive(move || tokens.get().is_some());

    // Setup token refresh interval
    setup_token_refresh(tokens, set_tokens, auth_service.clone());

    // Setup session timeout warning
    setup_session_timeout_warning(tokens);

    // Login action
    let login = create_action(move |(username, password): &(String, String)| {
        let auth_service = auth_service.clone();
        let username = username.clone();
        let password = password.clone();

        async move {
            match auth_service.login(username, password).await {
                Ok(response) => {
                    if !response.requires_mfa {
                        if let Some(tokens) = response.tokens {
                            set_tokens.set(Some(tokens));
                            Ok(())
                        } else {
                            Err("No tokens received".to_string())
                        }
                    } else {
                        Err("MFA required".to_string())
                    }
                }
                Err(e) => Err(e),
            }
        }
    });

    // Logout action
    let navigate = use_navigate();
    let logout = create_action(move |_: &()| {
        let auth_service = auth_service.clone();
        let current_tokens = tokens.get();

        async move {
            if let Some(tokens) = current_tokens {
                let _ = auth_service.logout(tokens.access_token).await;
            }
            set_tokens.set(None);
            navigate("/login", Default::default());
        }
    });

    AuthContext {
        tokens,
        set_tokens,
        is_authenticated,
        login,
        logout,
    }
}

pub fn use_auth_context() -> AuthContext {
    use_context::<AuthContext>().expect("AuthContext not provided")
}

// Automatic token refresh
fn setup_token_refresh(
    tokens: Signal<Option<AuthTokens>>,
    set_tokens: WriteSignal<Option<AuthTokens>>,
    auth_service: AuthService,
) {
    Effect::new(move |_| {
        if let Some(current_tokens) = tokens.get() {
            // Calculate time until token expires
            let expires_at = current_tokens.expires_at;
            let now = chrono::Utc::now().timestamp();
            let time_until_expiry = expires_at - now;

            // Refresh 5 minutes before expiry
            let refresh_in = time_until_expiry.saturating_sub(300);

            if refresh_in > 0 {
                // Schedule refresh
                let auth_service = auth_service.clone();
                let refresh_token = current_tokens.refresh_token.clone();

                set_timeout(
                    move || {
                        spawn_local(async move {
                            match auth_service.refresh_token(refresh_token).await {
                                Ok(new_tokens) => {
                                    set_tokens.set(Some(new_tokens));
                                    web_sys::console::log_1(&"Token refreshed successfully".into());
                                }
                                Err(e) => {
                                    web_sys::console::error_1(&format!("Token refresh failed: {}", e).into());
                                    // Clear tokens and redirect to login
                                    set_tokens.set(None);
                                }
                            }
                        });
                    },
                    Duration::from_secs(refresh_in as u64),
                );
            } else {
                // Token already expired or about to expire, refresh immediately
                let auth_service = auth_service.clone();
                let refresh_token = current_tokens.refresh_token.clone();

                spawn_local(async move {
                    match auth_service.refresh_token(refresh_token).await {
                        Ok(new_tokens) => {
                            set_tokens.set(Some(new_tokens));
                        }
                        Err(_) => {
                            set_tokens.set(None);
                        }
                    }
                });
            }
        }
    });
}

// Session timeout warning (5 minutes before expiry)
fn setup_session_timeout_warning(tokens: Signal<Option<AuthTokens>>) {
    Effect::new(move |_| {
        if let Some(current_tokens) = tokens.get() {
            let expires_at = current_tokens.expires_at;
            let now = chrono::Utc::now().timestamp();
            let time_until_expiry = expires_at - now;

            // Show warning 5 minutes before expiry
            let warning_in = time_until_expiry.saturating_sub(300);

            if warning_in > 0 {
                set_timeout(
                    move || {
                        // Show toast notification
                        web_sys::console::warn_1(&"Your session will expire in 5 minutes".into());

                        // In production, show a proper toast/modal
                        if let Some(window) = web_sys::window() {
                            let _ = window.alert_with_message(
                                "Your session will expire in 5 minutes. Please save your work.",
                            );
                        }
                    },
                    Duration::from_secs(warning_in as u64),
                );
            }
        }
    });
}
