import java.io.*;
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.zip.*;

public class DefendClassicUpdater {

    private static final String VERSION_URL = "https://defendandconquer.co.uk/wocfiles/version.json";
    private static final String LOCAL_VERSION_FILE = "client_version.txt";

    public static void main(String[] args) {
        try {
            Path appDir = Paths.get("").toAbsolutePath();

            System.out.println("Defend Classic Updater");
            System.out.println("Checking for updates...");

            String localVersion = readLocalVersion(appDir);
            String json = downloadText(VERSION_URL);

            String remoteVersion = extractJsonValue(json, "version");
            String zipUrl = extractJsonValue(json, "zip_url");
            String launchExe = extractJsonValue(json, "launch_exe");

            System.out.println("Installed version: " + localVersion);
            System.out.println("Latest version: " + remoteVersion);

            if (!remoteVersion.equals(localVersion)) {
                System.out.println("Update found. Downloading...");

                Path zipPath = appDir.resolve("update.zip");
                downloadFile(zipUrl, zipPath);

                System.out.println("Installing update...");
                unzip(zipPath, appDir);

                Files.writeString(appDir.resolve(LOCAL_VERSION_FILE), remoteVersion);

                Files.deleteIfExists(zipPath);

                System.out.println("Update complete.");
            } else {
                System.out.println("Client is already up to date.");
            }

            launchGame(appDir.resolve(launchExe));

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Updater failed. Press Enter to exit.");
            try { System.in.read(); } catch (IOException ignored) {}
        }
    }

    private static String readLocalVersion(Path appDir) throws IOException {
        Path file = appDir.resolve(LOCAL_VERSION_FILE);
        if (!Files.exists(file)) {
            Files.writeString(file, "0.0.0");
            return "0.0.0";
        }
        return Files.readString(file).trim();
    }

    private static String downloadText(String url) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();

        return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
    }

    private static void downloadFile(String url, Path target) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();

        client.send(request, HttpResponse.BodyHandlers.ofFile(target));
    }

    private static String extractJsonValue(String json, String key) {
        String search = "\"" + key + "\"";
        int start = json.indexOf(search);
        if (start == -1) return "";

        start = json.indexOf(":", start);
        start = json.indexOf("\"", start) + 1;
        int end = json.indexOf("\"", start);

        return json.substring(start, end);
    }

    private static void unzip(Path zipFile, Path targetDir) throws IOException {
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile.toFile()))) {
            ZipEntry entry;

            while ((entry = zis.getNextEntry()) != null) {
                Path newPath = targetDir.resolve(entry.getName()).normalize();

                if (!newPath.startsWith(targetDir)) {
                    throw new IOException("Blocked unsafe zip entry: " + entry.getName());
                }

                if (entry.isDirectory()) {
                    Files.createDirectories(newPath);
                } else {
                    Files.createDirectories(newPath.getParent());
                    Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING);
                }

                zis.closeEntry();
            }
        }
    }

    private static void launchGame(Path exePath) throws IOException {
        System.out.println("Launching game...");

        new ProcessBuilder(exePath.toString())
                .directory(exePath.getParent().toFile())
                .start();
    }
}