use leptos::prelude::*;
use serde::{Deserialize, Serialize};
use gloo_net::http::Request;
use crate::auth::storage::TokenStorage;
use crate::types::auth::*;

const API_BASE: &str = "http://localhost:8080/api/v1";

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LoginResponse {
    pub requires_mfa: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub partial_token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tokens: Option<AuthTokens>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MfaVerifyRequest {
    pub partial_token: String,
    pub code: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TotpEnrollResponse {
    pub secret: String,
    pub qr_code: String,
    pub manual_entry_code: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TotpVerifyRequest {
    pub code: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TotpVerifyResponse {
    pub success: bool,
    pub backup_codes: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WebAuthnChallengeResponse {
    pub challenge: String,
    pub rp_id: String,
    pub user_id: String,
    pub user_name: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WebAuthnCredential {
    pub id: String,
    pub raw_id: Vec<u8>,
    pub response: WebAuthnAuthenticatorResponse,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WebAuthnAuthenticatorResponse {
    pub attestation_object: Vec<u8>,
    pub client_data_json: Vec<u8>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MfaDevice {
    pub id: String,
    pub device_type: String,
    pub name: String,
    pub created_at: String,
    pub last_used: Option<String>,
}

pub struct AuthService {
    storage: TokenStorage,
}

impl AuthService {
    pub fn new() -> Self {
        Self {
            storage: TokenStorage::new(),
        }
    }

    /// Login with username and password
    pub async fn login(&self, username: String, password: String) -> Result<LoginResponse, String> {
        let request = LoginRequest { username, password };

        let response = Request::post(&format!("{}/auth/login", API_BASE))
            .json(&request)
            .map_err(|e| format!("Failed to serialize request: {}", e))?
            .send()
            .await
            .map_err(|e| format!("Login request failed: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("Login failed: {}", error_text));
        }

        response.json::<LoginResponse>()
            .await
            .map_err(|e| format!("Failed to parse response: {}", e))
    }

    /// Complete MFA login
    pub async fn complete_mfa_login(&self, partial_token: String, code: String) -> Result<AuthTokens, String> {
        let request = MfaVerifyRequest { partial_token, code };

        let response = Request::post(&format!("{}/auth/mfa/verify", API_BASE))
            .json(&request)
            .map_err(|e| format!("Failed to serialize request: {}", e))?
            .send()
            .await
            .map_err(|e| format!("MFA verification failed: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("MFA verification failed: {}", error_text));
        }

        let tokens = response.json::<AuthTokens>()
            .await
            .map_err(|e| format!("Failed to parse tokens: {}", e))?;

        // Store tokens
        self.storage.save_tokens(&tokens).map_err(|e| format!("Failed to save tokens: {}", e))?;

        Ok(tokens)
    }

    /// Refresh access token
    pub async fn refresh_token(&self, refresh_token: String) -> Result<AuthTokens, String> {
        let request = serde_json::json!({ "refresh_token": refresh_token });

        let response = Request::post(&format!("{}/auth/refresh", API_BASE))
            .json(&request)
            .map_err(|e| format!("Failed to serialize request: {}", e))?
            .send()
            .await
            .map_err(|e| format!("Token refresh failed: {}", e))?;

        if !response.ok() {
            return Err("Token refresh failed".to_string());
        }

        let tokens = response.json::<AuthTokens>()
            .await
            .map_err(|e| format!("Failed to parse tokens: {}", e))?;

        // Update stored tokens
        self.storage.save_tokens(&tokens).map_err(|e| format!("Failed to save tokens: {}", e))?;

        Ok(tokens)
    }

    /// Logout
    pub async fn logout(&self, access_token: String) -> Result<(), String> {
        let _ = Request::post(&format!("{}/auth/logout", API_BASE))
            .header("Authorization", &format!("Bearer {}", access_token))
            .send()
            .await;

        // Clear stored tokens
        self.storage.clear_tokens().map_err(|e| format!("Failed to clear tokens: {}", e))?;

        Ok(())
    }

    /// Start TOTP enrollment
    pub async fn enroll_totp(&self, access_token: String) -> Result<TotpEnrollResponse, String> {
        let response = Request::post(&format!("{}/auth/mfa/totp/enroll", API_BASE))
            .header("Authorization", &format!("Bearer {}", access_token))
            .send()
            .await
            .map_err(|e| format!("TOTP enrollment failed: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("TOTP enrollment failed: {}", error_text));
        }

        response.json::<TotpEnrollResponse>()
            .await
            .map_err(|e| format!("Failed to parse response: {}", e))
    }

    /// Verify TOTP code and complete enrollment
    pub async fn verify_totp(&self, access_token: String, code: String) -> Result<TotpVerifyResponse, String> {
        let request = TotpVerifyRequest { code };

        let response = Request::post(&format!("{}/auth/mfa/totp/verify", API_BASE))
            .header("Authorization", &format!("Bearer {}", access_token))
            .json(&request)
            .map_err(|e| format!("Failed to serialize request: {}", e))?
            .send()
            .await
            .map_err(|e| format!("TOTP verification failed: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("TOTP verification failed: {}", error_text));
        }

        response.json::<TotpVerifyResponse>()
            .await
            .map_err(|e| format!("Failed to parse response: {}", e))
    }

    /// Start WebAuthn registration
    pub async fn start_webauthn_registration(&self, access_token: String) -> Result<WebAuthnChallengeResponse, String> {
        let response = Request::post(&format!("{}/auth/mfa/webauthn/register/start", API_BASE))
            .header("Authorization", &format!("Bearer {}", access_token))
            .send()
            .await
            .map_err(|e| format!("WebAuthn registration start failed: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("WebAuthn registration start failed: {}", error_text));
        }

        response.json::<WebAuthnChallengeResponse>()
            .await
            .map_err(|e| format!("Failed to parse response: {}", e))
    }

    /// Finish WebAuthn registration
    pub async fn finish_webauthn_registration(&self, access_token: String, credential: WebAuthnCredential) -> Result<(), String> {
        let response = Request::post(&format!("{}/auth/mfa/webauthn/register/finish", API_BASE))
            .header("Authorization", &format!("Bearer {}", access_token))
            .json(&credential)
            .map_err(|e| format!("Failed to serialize credential: {}", e))?
            .send()
            .await
            .map_err(|e| format!("WebAuthn registration finish failed: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("WebAuthn registration finish failed: {}", error_text));
        }

        Ok(())
    }

    /// List MFA devices
    pub async fn list_mfa_devices(&self, access_token: String) -> Result<Vec<MfaDevice>, String> {
        let response = Request::get(&format!("{}/auth/mfa/devices", API_BASE))
            .header("Authorization", &format!("Bearer {}", access_token))
            .send()
            .await
            .map_err(|e| format!("Failed to list devices: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("Failed to list devices: {}", error_text));
        }

        response.json::<Vec<MfaDevice>>()
            .await
            .map_err(|e| format!("Failed to parse response: {}", e))
    }

    /// Remove MFA device
    pub async fn remove_mfa_device(&self, access_token: String, device_id: String) -> Result<(), String> {
        let response = Request::delete(&format!("{}/auth/mfa/devices/{}", API_BASE, device_id))
            .header("Authorization", &format!("Bearer {}", access_token))
            .send()
            .await
            .map_err(|e| format!("Failed to remove device: {}", e))?;

        if !response.ok() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            return Err(format!("Failed to remove device: {}", error_text));
        }

        Ok(())
    }

    /// Get stored tokens
    pub fn get_tokens(&self) -> Result<Option<AuthTokens>, String> {
        self.storage.get_tokens().map_err(|e| format!("Failed to get tokens: {}", e))
    }

    /// Check if user is authenticated
    pub fn is_authenticated(&self) -> bool {
        self.storage.get_tokens().ok().flatten().is_some()
    }
}

impl Default for AuthService {
    fn default() -> Self {
        Self::new()
    }
}
