use leptos::prelude::*;
use rust_shared::repository::model::Profile;
use serde::{Deserialize, Serialize};
use base64::{Engine as _, engine::general_purpose};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JwtPayload {
    pub sub: Option<String>,
    pub exp: Option<u64>,
    pub iat: Option<u64>,
    pub role: Option<String>,
    pub user_id: Option<String>,
    pub email: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserData {
    pub token: String,
    pub profile: Profile,
}

#[derive(Clone, Debug)]
pub struct AuthState {
    pub is_authenticated: RwSignal<bool>,
    pub user_data: RwSignal<Option<UserData>>,
}

impl AuthState {
    /// Decode JWT token and extract payload
    fn decode_jwt_payload(token: &str) -> Option<JwtPayload> {
        let parts: Vec<&str> = token.split('.').collect();
        if parts.len() != 3 {
            log::warn!("Invalid JWT token format");
            return None;
        }
        
        // Decode the payload (second part)
        let payload_part = parts[1];
        
        // Add padding if needed
        let mut padded = payload_part.to_string();
        while padded.len() % 4 != 0 {
            padded.push('=');
        }
        
        match general_purpose::STANDARD.decode(&padded) {
            Ok(decoded_bytes) => {
                match String::from_utf8(decoded_bytes) {
                    Ok(payload_str) => {
                        match serde_json::from_str::<JwtPayload>(&payload_str) {
                            Ok(payload) => Some(payload),
                            Err(e) => {
                                log::warn!("Failed to parse JWT payload: {}", e);
                                None
                            }
                        }
                    }
                    Err(e) => {
                        log::warn!("Failed to convert JWT payload to string: {}", e);
                        None
                    }
                }
            }
            Err(e) => {
                log::warn!("Failed to decode JWT payload: {}", e);
                None
            }
        }
    }
    
    /// Check if current user is admin based on JWT role
    pub fn is_admin(&self) -> bool {
        if let Some(token) = self.get_token() {
            if let Some(payload) = Self::decode_jwt_payload(&token) {
                return payload.role.as_ref().map_or(false, |role| role == "vgate_admin");
            }
        }
        false
    }
    
    pub fn new() -> Self {
        Self {
            is_authenticated: RwSignal::new(false),
            user_data: RwSignal::new(None),
        }
    }
    
    pub fn login(&self, token: String, profile: Profile) {
        let user_data = UserData { token, profile };
        self.user_data.set(Some(user_data.clone()));
        self.is_authenticated.set(true);
        log::info!("User logged in: {}", user_data.profile.id);
        
        #[cfg(feature = "hydrate")]
        {
            if let Some(window) = web_sys::window() {
                if let Ok(Some(storage)) = window.local_storage() {
                    if let Ok(user_data_json) = serde_json::to_string(&user_data) {
                        let _ = storage.set_item("auth_user_data", &user_data_json);
                        let _ = storage.set_item("auth_authenticated", "true");
                    }
                }
            }
        }
    }
    
    pub fn logout(&self) {
        self.user_data.set(None);
        self.is_authenticated.set(false);
        log::info!("User logged out");
        
        #[cfg(feature = "hydrate")]
        {
            if let Some(window) = web_sys::window() {
                if let Ok(Some(storage)) = window.local_storage() {
                    let _ = storage.remove_item("auth_user_data");
                    let _ = storage.remove_item("auth_authenticated");
                }
            }
        }
    }
    
    pub fn restore_from_storage(&self) {
        #[cfg(feature = "hydrate")]
        {
            if let Some(window) = web_sys::window() {
                if let Ok(Some(storage)) = window.local_storage() {
                    if let Ok(Some(authenticated)) = storage.get_item("auth_authenticated") {
                        if authenticated == "true" {
                            if let Ok(Some(user_data_json)) = storage.get_item("auth_user_data") {
                                if let Ok(user_data) = serde_json::from_str::<UserData>(&user_data_json) {
                                    self.user_data.set(Some(user_data.clone()));
                                    self.is_authenticated.set(true);
                                    log::info!("Auth restored from storage: {}", user_data.profile.id);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    /// Get current user's email
    pub fn get_user_email(&self) -> Option<String> {
        self.user_data.get().and_then(|data| data.profile.email.clone())
    }
    
    /// Get current JWT token
    pub fn get_token(&self) -> Option<String> {
        self.user_data.get().map(|data| data.token.clone())
    }
    
    /// Get current user profile
    pub fn get_profile(&self) -> Option<Profile> {
        self.user_data.get().map(|data| data.profile.clone())
    }
    
    /// Check if the user is authenticated and has a valid token
    pub fn is_valid_session(&self) -> bool {
        self.is_authenticated.get() && self.get_token().is_some()
    }
    
    /// Force logout if session is invalid (e.g., token expired)
    /// This should be called sparingly to avoid infinite loops
    pub fn validate_session_or_logout(&self) {
        if self.is_authenticated.get() && self.get_token().is_none() {
            log::warn!("Invalid session detected (missing token), forcing logout");
            self.logout();
        }
    }
    
    /// Refresh profile data from API
    pub async fn refresh_profile(&self) -> Result<(), String> {
        if let Some(token) = self.get_token() {
            match crate::api::fetch_user_profile(&token).await {
                Ok(profile) => {
                    let user_data = UserData { token, profile };
                    self.user_data.set(Some(user_data.clone()));
                    
                    #[cfg(feature = "hydrate")]
                    {
                        if let Some(window) = web_sys::window() {
                            if let Ok(Some(storage)) = window.local_storage() {
                                if let Ok(user_data_json) = serde_json::to_string(&user_data) {
                                    let _ = storage.set_item("auth_user_data", &user_data_json);
                                }
                            }
                        }
                    }
                    
                    log::info!("Profile refreshed successfully");
                    Ok(())
                }
                Err(e) => {
                    log::error!("Failed to refresh profile: {}", e);
                    Err(format!("Failed to refresh profile: {}", e))
                }
            }
        } else {
            Err("No authentication token available".to_string())
        }
    }

    /// Get token from localStorage without AuthState instance
    pub fn get_token_from_storage() -> Option<String> {
        #[cfg(feature = "hydrate")]
        {
            if let Some(window) = web_sys::window() {
                if let Ok(Some(storage)) = window.local_storage() {
                    if let Ok(Some(user_data_json)) = storage.get_item("auth_user_data") {
                        if let Ok(user_data) = serde_json::from_str::<UserData>(&user_data_json) {
                            return Some(user_data.token);
                        }
                    }
                }
            }
        }
        None
    }
}

pub fn provide_auth_context() -> AuthState {
    let auth_state = AuthState::new();
    provide_context(auth_state.clone());
    auth_state
}

pub fn use_auth() -> AuthState {
    expect_context::<AuthState>()
}
