//! language: Rust, file: apexauth.rs, target: Rust 1.70+ (Windows / Linux / macOS)
//! Universal native ApexAuth SDK for Rust applications.
//! The SDK file is static and must NOT be edited. Provide credentials at initialization.

use std::collections::HashMap;
use std::process::Command;

#[derive(Debug, Clone, Default)]
pub struct UserData {
    pub username: String,
    pub ip: String,
    pub hwid: String,
    pub createdate: String,
    pub lastlogin: String,
    pub expiry: String,
    pub subscriptions: Vec<String>,
}

#[derive(Debug, Clone, Default)]
pub struct ApiResponse {
    pub success: bool,
    pub message: String,
}

#[derive(Debug, Clone)]
pub struct ApexAuth {
    pub name: String,
    pub ownerid: String,
    pub secret: String,
    pub version: String,
    pub url: String,
    pub user_data: UserData,
    pub response: ApiResponse,
    session_token: String,
    enc_key: String,
}

impl ApexAuth {
    /// Initialize universal ApexAuth client instance.
    pub fn new(name: &str, ownerid: &str, secret: &str, version: &str, url: &str) -> Self {
        let mut clean_url = url.trim_end_matches('/').to_string();
        if clean_url.ends_with("/api/v1") {
            clean_url = clean_url[..clean_url.len() - 7].to_string();
        }

        Self {
            name: name.to_string(),
            ownerid: ownerid.to_string(),
            secret: secret.to_string(),
            version: version.to_string(),
            url: clean_url,
            user_data: UserData::default(),
            response: ApiResponse::default(),
            session_token: String::new(),
            enc_key: String::new(),
        }
    }

    /// Retrieve local machine Hardware ID (HWID).
    pub fn get_hwid() -> String {
        #[cfg(target_os = "windows")]
        {
            if let Ok(output) = Command::new("powershell")
                .args(["-NoProfile", "-Command", "(Get-CimInstance -Class Win32_ComputerSystemProduct).UUID"])
                .output()
            {
                let text = String::from_utf8_lossy(&output.stdout).trim().to_string();
                if !text.is_empty() {
                    return text;
                }
            }

            if let Ok(output) = Command::new("wmic")
                .args(["csproduct", "get", "uuid"])
                .output()
            {
                let text = String::from_utf8_lossy(&output.stdout);
                let lines: Vec<&str> = text.lines().map(|s| s.trim()).filter(|s| !s.is_empty()).collect();
                if lines.len() >= 2 {
                    return lines[1].to_string();
                }
            }
        }

        #[cfg(target_os = "macos")]
        {
            if let Ok(output) = Command::new("sh")
                .arg("-c")
                .arg("ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { print $3; }'")
                .output()
            {
                let text = String::from_utf8_lossy(&output.stdout).trim().trim_matches('"').to_string();
                if !text.is_empty() {
                    return text;
                }
            }
        }

        #[cfg(target_os = "linux")]
        {
            if let Ok(uuid) = std::fs::read_to_string("/etc/machine-id") {
                let trimmed = uuid.trim().to_string();
                if !trimmed.is_empty() {
                    return trimmed;
                }
            }
        }

        "UNKNOWN-HWID-NODE".to_string()
    }

    /// Internal HTTP POST dispatcher via curl fallback for zero external cargo crate requirements.
    fn perform_request(&self, endpoint: &str, json_payload: &str) -> Result<String, String> {
        let target_url = format!("{}/api/v1{}", self.url, endpoint);

        let output = Command::new("curl")
            .args([
                "-s",
                "-X", "POST",
                "-H", "Content-Type: application/json",
                "-H", &format!("x-app-secret: {}", self.secret),
                "-H", &format!("x-owner-id: {}", self.ownerid),
                "-H", &format!("x-app-name: {}", self.name),
                "-d", json_payload,
                &target_url,
            ])
            .output()
            .map_err(|e| format!("Failed to dispatch HTTP request: {}", e))?;

        if !output.status.success() {
            return Err("HTTP transport error occurred.".to_string());
        }

        Ok(String::from_utf8_lossy(&output.stdout).to_string())
    }

    /// Quick JSON string extractor helper (zero external dependencies).
    fn extract_json_str(json: &str, key: &str) -> String {
        let pattern = format!("\"{}\":\"", key);
        if let Some(pos) = json.find(&pattern) {
            let start = pos + pattern.len();
            if let Some(end) = json[start..].find('"') {
                return json[start..start + end].to_string();
            }
        }
        let pattern_no_quote = format!("\"{}\":", key);
        if let Some(pos) = json.find(&pattern_no_quote) {
            let start = pos + pattern_no_quote.len();
            let slice = json[start..].trim_start();
            let end = slice.find(|c| c == ',' || c == '}' || c == ']').unwrap_or(slice.len());
            return slice[..end].trim().trim_matches('"').to_string();
        }
        String::new()
    }

    fn extract_json_bool(json: &str, key: &str) -> bool {
        let pattern = format!("\"{}\":", key);
        if let Some(pos) = json.find(&pattern) {
            let start = pos + pattern_no_quote(&pattern).len();
            let slice = json[start..].trim_start().to_lowercase();
            return slice.starts_with("true");
        }
        false
    }

    /// Handshake with ApexAuth cluster and initialize encrypted session.
    pub fn init(&mut self) -> &ApiResponse {
        let body = format!(
            r#"{{"name":"{}","ownerid":"{}","secret":"{}","version":"{}"}}"#,
            self.name, self.ownerid, self.secret, self.version
        );

        match self.perform_request("/init", &body) {
            Ok(raw) => {
                let success = raw.contains("\"success\":true") || raw.contains("\"success\": true");
                let message = Self::extract_json_str(&raw, "message");
                self.response = ApiResponse {
                    success,
                    message: if message.is_empty() { "Init completed".into() } else { message },
                };

                if self.response.success {
                    self.enc_key = Self::extract_json_str(&raw, "encryption_key");
                    let sess = Self::extract_json_str(&raw, "session_token");
                    self.session_token = if sess.is_empty() {
                        Self::extract_json_str(&raw, "sessionid")
                    } else {
                        sess
                    };
                }
            }
            Err(e) => {
                self.response = ApiResponse {
                    success: false,
                    message: e,
                };
            }
        }

        &self.response
    }

    /// Authenticate via single License Key (HWID lock bound on first authorization).
    pub fn license(&mut self, key: &str) -> &ApiResponse {
        let hwid = Self::get_hwid();
        let body = format!(r#"{{"license":"{}","hwid":"{}"}}"#, key, hwid);

        match self.perform_request("/license", &body) {
            Ok(raw) => self.parse_auth_response(&raw),
            Err(e) => {
                self.response = ApiResponse { success: false, message: e };
            }
        }

        &self.response
    }

    /// Authenticate with Username and Password.
    pub fn login(&mut self, username: &str, password: &str) -> &ApiResponse {
        let hwid = Self::get_hwid();
        let body = format!(
            r#"{{"username":"{}","password":"{}","hwid":"{}"}}"#,
            username, password, hwid
        );

        match self.perform_request("/login", &body) {
            Ok(raw) => self.parse_auth_response(&raw),
            Err(e) => {
                self.response = ApiResponse { success: false, message: e };
            }
        }

        &self.response
    }

    /// Register a new account with license key.
    pub fn register(&mut self, username: &str, password: &str, key: &str, email: Option<&str>) -> &ApiResponse {
        let hwid = Self::get_hwid();
        let mail = email.unwrap_or(&format!("{}@client.local", username));
        let body = format!(
            r#"{{"username":"{}","password":"{}","license":"{}","email":"{}","hwid":"{}"}}"#,
            username, password, key, mail, hwid
        );

        match self.perform_request("/register", &body) {
            Ok(raw) => self.parse_auth_response(&raw),
            Err(e) => {
                self.response = ApiResponse { success: false, message: e };
            }
        }

        &self.response
    }

    /// Decrypt cloud variable value stored securely on server.
    pub fn var(&self, var_id: &str) -> Result<String, String> {
        let body = format!(
            r#"{{"varid":"{}","session_token":"{}"}}"#,
            var_id, self.session_token
        );

        let raw = self.perform_request("/var", &body)?;
        let success = raw.contains("\"success\":true") || raw.contains("\"success\": true");
        if success {
            let val = Self::extract_json_str(&raw, "var_value");
            if !val.is_empty() {
                return Ok(val);
            }
            let data_val = Self::extract_json_str(&raw, "value");
            return Ok(data_val);
        }

        Err(Self::extract_json_str(&raw, "message"))
    }

    /// Check and verify active session validity.
    pub fn check(&mut self) -> &ApiResponse {
        let body = format!(r#"{{"session_token":"{}"}}"#, self.session_token);
        match self.perform_request("/check", &body) {
            Ok(raw) => {
                let success = raw.contains("\"success\":true") || raw.contains("\"success\": true");
                self.response = ApiResponse {
                    success,
                    message: Self::extract_json_str(&raw, "message"),
                };
            }
            Err(e) => {
                self.response = ApiResponse { success: false, message: e };
            }
        }
        &self.response
    }

    fn parse_auth_response(&mut self, raw: &str) {
        let success = raw.contains("\"success\":true") || raw.contains("\"success\": true");
        let message = Self::extract_json_str(raw, "message");

        self.response = ApiResponse {
            success,
            message: if message.is_empty() { "Authentication completed".into() } else { message },
        };

        if self.response.success {
            self.user_data.username = Self::extract_json_str(raw, "username");
            self.user_data.ip = Self::extract_json_str(raw, "ip");
            self.user_data.hwid = Self::extract_json_str(raw, "hwid");
            self.user_data.createdate = Self::extract_json_str(raw, "createdate");
            self.user_data.lastlogin = Self::extract_json_str(raw, "lastlogin");
            let mut exp = Self::extract_json_str(raw, "expires");
            if exp.is_empty() {
                exp = Self::extract_json_str(raw, "expires_at");
            }
            self.user_data.expiry = exp;

            let token = Self::extract_json_str(raw, "session_token");
            if !token.is_empty() {
                self.session_token = token;
            }
        }
    }
}

fn pattern_no_quote(p: &str) -> &str {
    p
}
