package apexauth;

// language: Java, file: ApexAuth.java, target: Java 11+
// Zero-dependency native Java SDK for ApexAuth.

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class ApexAuth {
    public String name;
    public String ownerid;
    public String secret;
    public String version;
    public String url;

    public user_data_class user_data = new user_data_class();
    public response_class response = new response_class();

    private final HttpClient client;
    private String sessionToken = "";
    private String encKey = "";

    public static class user_data_class {
        public String username = "";
        public String ip = "";
        public String hwid = "";
        public String createdate = "";
        public String lastlogin = "";
        public String expiry = "";
        public List<String> subscriptions = new ArrayList<>();
    }

    public static class response_class {
        public boolean success = false;
        public String message = "";
    }

    public ApexAuth(String name, String ownerid, String secret, String version, String url) {
        this.name = name;
        this.ownerid = ownerid;
        this.secret = secret;
        this.version = version;
        String u = (url != null) ? url.replaceAll("/+$", "") : "";
        if (u.toLowerCase().endsWith("/api/v1")) {
            u = u.substring(0, u.length() - 7);
        }
        this.url = u;
        this.client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(15))
                .build();
    }

    public static String getHwid() {
        String mainInfo = System.getProperty("os.name") + System.getProperty("os.arch") + System.getProperty("user.name");
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(mainInfo.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            return "java-hwid-" + Math.abs(mainInfo.hashCode());
        }
    }

    private String calculateHmacSha256(String data, String key) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : rawHmac) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            return "";
        }
    }

    private String extractJsonString(String json, String key) {
        Pattern pattern = Pattern.compile("\"" + Pattern.quote(key) + "\"\\s*:\\s*\"([^\"]*)\"");
        Matcher matcher = pattern.matcher(json);
        return matcher.find() ? matcher.group(1) : "";
    }

    private boolean extractJsonBool(String json, String key) {
        Pattern pattern = Pattern.compile("\"" + Pattern.quote(key) + "\"\\s*:\\s*(true|false)", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(json);
        return matcher.find() && Boolean.parseBoolean(matcher.group(1));
    }

    private String postRaw(String endpoint, String jsonBody, boolean bypassSign) {
        try {
            String fullUrl = this.url + endpoint;
            String timestamp = String.valueOf(Instant.now().getEpochSecond());
            String nonce = UUID.randomUUID().toString().replace("-", "").substring(0, 16);

            HttpRequest.Builder builder = HttpRequest.newBuilder()
                    .uri(URI.create(fullUrl))
                    .timeout(Duration.ofSeconds(15))
                    .header("Content-Type", "application/json")
                    .header("x-app-secret", this.secret)
                    .header("x-owner-id", this.ownerid)
                    .header("x-app-name", this.name)
                    .header("x-timestamp", timestamp)
                    .header("x-nonce", nonce)
                    .header("User-Agent", "ApexAuth-Java/" + this.version);

            if (!bypassSign) {
                String signature = calculateHmacSha256(jsonBody + timestamp + nonce, this.secret);
                builder.header("x-signature", signature);
            }

            if (jsonBody != null && !jsonBody.isEmpty()) {
                builder.POST(HttpRequest.BodyPublishers.ofString(jsonBody, StandardCharsets.UTF_8));
            } else {
                builder.POST(HttpRequest.BodyPublishers.noBody());
            }

            HttpResponse<String> httpResponse = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
            return httpResponse.body();
        } catch (Exception e) {
            this.response.success = false;
            this.response.message = "Network error: " + e.getMessage();
            return "{\"success\":false,\"message\":\"" + e.getMessage() + "\"}";
        }
    }

    private void parseResponse(String raw) {
        this.response.success = extractJsonBool(raw, "success");
        this.response.message = extractJsonString(raw, "message");

        if (raw.contains("\"session_token\"") || raw.contains("\"sessionid\"")) {
            String tok = extractJsonString(raw, "session_token");
            if (tok.isEmpty()) tok = extractJsonString(raw, "sessionid");
            if (!tok.isEmpty()) this.sessionToken = tok;
        }

        if (raw.contains("\"encryption_key\"")) {
            this.encKey = extractJsonString(raw, "encryption_key");
        }

        if (this.response.success && (raw.contains("\"hwid\"") || raw.contains("\"subscriptions\""))) {
            this.user_data.username = extractJsonString(raw, "username");
            this.user_data.ip = extractJsonString(raw, "ip");
            this.user_data.hwid = extractJsonString(raw, "hwid");
            this.user_data.createdate = extractJsonString(raw, "createdate");
            this.user_data.lastlogin = extractJsonString(raw, "lastlogin");
            this.user_data.expiry = extractJsonString(raw, "expires");

            this.user_data.subscriptions.clear();
            Pattern p = Pattern.compile("\"subscription\"\\s*:\\s*\"([^\"]+)\"");
            Matcher m = p.matcher(raw);
            while (m.find()) {
                this.user_data.subscriptions.add(m.group(1));
            }
        }
    }

    // ==========================================
    // CORE AUTHENTICATION PUBLIC METHODS
    // ==========================================

    public void init() {
        String body = String.format("{\"name\":\"%s\",\"ownerid\":\"%s\",\"secret\":\"%s\",\"version\":\"%s\"}",
                name, ownerid, secret, version);
        String res = postRaw("/api/v1/init", body, true);
        parseResponse(res);
    }

    public void register(String username, String password, String licenseKey, String email) {
        String body = String.format("{\"username\":\"%s\",\"email\":\"%s\",\"password\":\"%s\",\"license\":\"%s\",\"hwid\":\"%s\"}",
                username, (email != null ? email : ""), password, licenseKey, getHwid());
        String res = postRaw("/api/v1/register", body, false);
        parseResponse(res);
    }

    public void register(String username, String password, String licenseKey) {
        register(username, password, licenseKey, "");
    }

    public void login(String username, String password) {
        String body = String.format("{\"username\":\"%s\",\"password\":\"%s\",\"hwid\":\"%s\"}",
                username, password, getHwid());
        String res = postRaw("/api/v1/login", body, false);
        parseResponse(res);
    }

    public void license(String key) {
        String body = String.format("{\"license\":\"%s\",\"hwid\":\"%s\"}", key, getHwid());
        String res = postRaw("/api/v1/license", body, false);
        parseResponse(res);
    }

    public void check() {
        String body = String.format("{\"session_token\":\"%s\",\"hwid\":\"%s\"}", sessionToken, getHwid());
        String res = postRaw("/api/v1/check", body, false);
        parseResponse(res);
    }

    public String var(String varName) {
        String body = String.format("{\"name\":\"%s\"}", varName);
        String res = postRaw("/api/v1/var", body, false);
        parseResponse(res);
        return extractJsonString(res, "value");
    }

    public void log(String message) {
        String body = String.format("{\"message\":\"%s\"}", message);
        String res = postRaw("/api/v1/log", body, false);
        parseResponse(res);
    }

    public void ban(String reason) {
        String body = String.format("{\"reason\":\"%s\",\"hwid\":\"%s\"}", (reason != null ? reason : ""), getHwid());
        String res = postRaw("/api/v1/ban", body, false);
        parseResponse(res);
    }

    public byte[] file(int fileId) {
        String body = String.format("{\"file_id\":%d}", fileId);
        String res = postRaw("/api/v1/file", body, false);
        parseResponse(res);

        String fileUrl = extractJsonString(res, "download_url");
        if (fileUrl.isEmpty()) return new byte[0];

        if (!fileUrl.startsWith("http")) {
            fileUrl = this.url + "/" + fileUrl.replaceAll("^/", "");
        }

        try {
            HttpRequest req = HttpRequest.newBuilder().uri(URI.create(fileUrl)).build();
            HttpResponse<byte[]> resp = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
            return resp.body();
        } catch (Exception e) {
            return new byte[0];
        }
    }

    public static class ApexAuthApp extends ApexAuth {
        public ApexAuthApp(String name, String ownerid, String secret, String version, String url) {
            super(name, ownerid, secret, version, url);
        }
    }

    public static class api extends ApexAuth {
        public api(String name, String ownerid, String secret, String version, String url) {
            super(name, ownerid, secret, version, url);
        }
    }
}
