late-java-core 2.2.9

A Rust library for launching Minecraft Java Edition
use super::*;
use crate::error::{Result, LateJavaCoreError};
use reqwest::Client;
use serde_json::json;
use uuid::Uuid;

/// Autenticador AZauth (servidor Yggdrasil compatible)
pub struct AZauthAuth {
    base_url: String,
    client: Client,
}

impl AZauthAuth {
    /// Crear un nuevo autenticador AZauth
    pub fn new(base_url: String) -> Self {
        Self {
            base_url,
            client: Client::new(),
        }
    }

    /// Autenticar con usuario y contraseƱa
    pub async fn authenticate(&self, username: &str, password: &str) -> Result<AuthResponse> {
        let response = self.client
            .post(&format!("{}/authserver/authenticate", self.base_url))
            .json(&json!({
                "agent": {
                    "name": "Minecraft",
                    "version": 1
                },
                "username": username,
                "password": password,
                "clientToken": Uuid::new_v4().to_string()
            }))
            .send()
            .await?;

        let auth_response: serde_json::Value = response.json().await?;
        
        if let Some(error) = auth_response.get("error") {
            return Err(LateJavaCoreError::Auth(format!("AZauth error: {}", error)));
        }

        let selected_profile = auth_response["selectedProfile"]
            .as_object()
            .ok_or_else(|| LateJavaCoreError::Auth("No selected profile".to_string()))?;

        Ok(AuthResponse {
            access_token: auth_response["accessToken"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            client_token: auth_response["clientToken"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            uuid: selected_profile["id"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            name: selected_profile["name"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            refresh_token: None,
            user_properties: auth_response["user"]
                .get("properties")
                .map(|p| p.to_string())
                .unwrap_or_else(|| "{}".to_string()),
            meta: AuthMeta {
                auth_type: "AZauth".to_string(),
                access_token_expires_in: chrono::Utc::now().timestamp() as u64 + 86400, // 24 horas
                demo: false,
            },
            xbox_account: None,
            profile: None,
        })
    }

    /// Refrescar token
    pub async fn refresh(&self, auth: AuthResponse) -> Result<AuthResponse> {
        let response = self.client
            .post(&format!("{}/authserver/refresh", self.base_url))
            .json(&json!({
                "accessToken": auth.access_token,
                "clientToken": auth.client_token
            }))
            .send()
            .await?;

        let auth_response: serde_json::Value = response.json().await?;
        
        if let Some(error) = auth_response.get("error") {
            return Err(LateJavaCoreError::Auth(format!("AZauth refresh error: {}", error)));
        }

        let selected_profile = auth_response["selectedProfile"]
            .as_object()
            .ok_or_else(|| LateJavaCoreError::Auth("No selected profile".to_string()))?;

        Ok(AuthResponse {
            access_token: auth_response["accessToken"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            client_token: auth_response["clientToken"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            uuid: selected_profile["id"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            name: selected_profile["name"]
                .as_str()
                .unwrap_or("")
                .to_string(),
            refresh_token: None,
            user_properties: auth_response["user"]
                .get("properties")
                .map(|p| p.to_string())
                .unwrap_or_else(|| "{}".to_string()),
            meta: AuthMeta {
                auth_type: "AZauth".to_string(),
                access_token_expires_in: chrono::Utc::now().timestamp() as u64 + 86400,
                demo: false,
            },
            xbox_account: None,
            profile: None,
        })
    }

    /// Validar token
    pub async fn validate(&self, access_token: &str, client_token: &str) -> Result<bool> {
        let response = self.client
            .post(&format!("{}/authserver/validate", self.base_url))
            .json(&json!({
                "accessToken": access_token,
                "clientToken": client_token
            }))
            .send()
            .await?;

        Ok(response.status().is_success())
    }

    /// Invalidar token
    pub async fn invalidate(&self, access_token: &str, client_token: &str) -> Result<()> {
        self.client
            .post(&format!("{}/authserver/invalidate", self.base_url))
            .json(&json!({
                "accessToken": access_token,
                "clientToken": client_token
            }))
            .send()
            .await?;

        Ok(())
    }
}