package de.winniepat.citrus.managers;

import com.google.gson.*;
import de.winniepat.citrus.Client;
import de.winniepat.citrus.utils.SecurityUtils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.session.Session;
import net.minecraft.util.Util;

import java.io.OutputStream;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;

public class ClientRegistrationManager {
    private static final String API_BASE_URL = Client.API_URL;
    private static final Gson GSON = new GsonBuilder().create();

    private static boolean isRegistered = false;
    private static boolean isShuttingDown = false;
    private static Thread heartbeatThread;

    private static final Set<UUID> ONLINE_CITRUS_USERS = new HashSet<>();
    private static long lastOnlineListUpdate = 0;
    private static final long ONLINE_LIST_CACHE_TIME = 30000;

    public static void register() {
        MinecraftClient client = MinecraftClient.getInstance();

        Session session = client.getSession();
        if (session == null) {
            Client.logger.error("Cannot register: No session available");
            return;
        }

        String playerName = session.getUsername();
        UUID playerUUID = session.getUuidOrNull();

        if (playerUUID == null) {
            Client.logger.error("Cannot register: No UUID in session");
            return;
        }

        Client.debugLog("Registering as online user: " + playerName + " (" + playerUUID + ")");

        CompletableFuture.runAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/client/register").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                conn.setDoOutput(true);
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);

                JsonObject json = new JsonObject();
                json.addProperty("uuid", playerUUID.toString());
                json.addProperty("name", playerName);

                try (OutputStream os = conn.getOutputStream()) {
                    os.write(GSON.toJson(json).getBytes());
                    os.flush();
                }

                if (conn.getResponseCode() == 200) {
                    isRegistered = true;

                    Client.debugLog("Successfully registered as online user");

                    startHeartbeat();

                    updateOnlineUsersList();
                } else {
                    Client.logger.error("Failed to register: HTTP {}", conn.getResponseCode());
                }

                conn.disconnect();
            } catch (Exception e) {
                Client.logger.error("Registration failed: ", e);
            }
        }, Util.getMainWorkerExecutor());
    }

    public static void updatePlayerInfo() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player == null) return;

        if (isRegistered) {
            UUID worldUUID = client.player.getUuid();
            String worldName = client.player.getName().getString();

            CompletableFuture.runAsync(() -> {
                try {
                    URL url = new URI(API_BASE_URL + "/client/register").toURL();
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("POST");
                    conn.setRequestProperty("Content-Type", "application/json");
                    conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                    conn.setDoOutput(true);
                    conn.setConnectTimeout(3000);

                    JsonObject json = new JsonObject();
                    json.addProperty("uuid", worldUUID.toString());
                    json.addProperty("name", worldName);

                    try (OutputStream os = conn.getOutputStream()) {
                        os.write(GSON.toJson(json).getBytes());
                        os.flush();
                    }

                    if (conn.getResponseCode() == 200) {
                        Client.debugLog("Updated player info for world: " + worldName);
                    }

                    conn.disconnect();
                } catch (Exception e) {
                    Client.logger.error("Error updating player info: ", e);
                }
            });
        }
    }

    private static void startHeartbeat() {
        if (heartbeatThread != null && heartbeatThread.isAlive()) {
            heartbeatThread.interrupt();
        }

        heartbeatThread = new Thread(() -> {
            while (!isShuttingDown && isRegistered) {
                try {
                    Thread.sleep(30000);
                    sendHeartbeat();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }, "Citrus-Heartbeat");

        heartbeatThread.setDaemon(true);
        heartbeatThread.start();
    }

    private static void sendHeartbeat() {
        if (!isRegistered) return;

        UUID uuid = getCurrentUUID();
        if (uuid == null) return;

        CompletableFuture.runAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/client/heartbeat").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                conn.setDoOutput(true);
                conn.setConnectTimeout(3000);
                conn.setReadTimeout(3000);

                JsonObject json = new JsonObject();
                json.addProperty("uuid", uuid.toString());

                try (OutputStream os = conn.getOutputStream()) {
                    os.write(GSON.toJson(json).getBytes());
                    os.flush();
                }

                if (conn.getResponseCode() != 200) {
                    Client.logger.error("Heartbeat failed: HTTP {} {}", conn.getResponseCode(), conn.getResponseMessage());
                }

                conn.disconnect();

            } catch (Exception e) {
                Client.logger.error("Heartbeat failed: ", e);
            }
        });
    }

    public static void unregister() {
        if (!isRegistered) return;

        isShuttingDown = true;

        UUID uuid = getCurrentUUID();
        if (uuid == null) return;

        Client.debugLog("Unregistering from online users...");

        CompletableFuture.runAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/client/unregister").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                conn.setDoOutput(true);
                conn.setConnectTimeout(3000);
                conn.setReadTimeout(3000);

                JsonObject json = new JsonObject();
                json.addProperty("uuid", uuid.toString());

                try (OutputStream os = conn.getOutputStream()) {
                    os.write(GSON.toJson(json).getBytes());
                    os.flush();
                }

                Client.debugLog("Unregistered successfully");

                conn.disconnect();

            } catch (Exception e) {
                Client.logger.error("Unregistration failed: ", e);
            }
        });
    }

    private static UUID getCurrentUUID() {
        MinecraftClient client = MinecraftClient.getInstance();

        if (client.player != null) {
            return client.player.getUuid();
        }

        Session session = client.getSession();
        if (session != null) {
            return session.getUuidOrNull();
        }

        return null;
    }

    public static List<UUID> getOnlineCitrusUsers() {
        if (System.currentTimeMillis() - lastOnlineListUpdate >= ONLINE_LIST_CACHE_TIME) {
            updateOnlineUsersList();
        }

        synchronized (ONLINE_CITRUS_USERS) {
            return new ArrayList<>(ONLINE_CITRUS_USERS);
        }
    }

    private static void updateOnlineUsersList() {
        CompletableFuture.runAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/client/online").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);

                if (conn.getResponseCode() == 200) {
                    String response = new String(conn.getInputStream().readAllBytes());
                    JsonObject[] users = GSON.fromJson(response, JsonObject[].class);

                    List<UUID> newList = new ArrayList<>();
                    if (users != null) {
                        for (JsonObject user : users) {
                            try {
                                UUID uuid = UUID.fromString(user.get("player_uuid").getAsString());
                                newList.add(uuid);
                            } catch (Exception e) {
                                Client.logger.error("Invalid UUID in online list: {}", user);
                            }
                        }
                    }

                    synchronized (ONLINE_CITRUS_USERS) {
                        ONLINE_CITRUS_USERS.clear();
                        ONLINE_CITRUS_USERS.addAll(newList);
                    }

                    lastOnlineListUpdate = System.currentTimeMillis();

                    Client.debugLog("Updated online users list: " + newList.size() + " users");
                }

                conn.disconnect();
            } catch (Exception e) {
                Client.logger.error("Failed to update online users list: {}", String.valueOf(e));
            }
        });
    }

    public static List<UUID> filterOnlineUsers(List<UUID> playerUUIDs) {
        if (playerUUIDs.isEmpty()) return new ArrayList<>();

        CompletableFuture<List<UUID>> future = CompletableFuture.supplyAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/client/online-check").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                conn.setDoOutput(true);
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);

                List<String> uuidStrings = new ArrayList<>();
                for (UUID uuid : playerUUIDs) {
                    uuidStrings.add(uuid.toString());
                }

                JsonObject json = new JsonObject();
                json.add("uuids", GSON.toJsonTree(uuidStrings));

                try (OutputStream os = conn.getOutputStream()) {
                    os.write(GSON.toJson(json).getBytes());
                    os.flush();
                }

                if (conn.getResponseCode() == 200) {
                    String response = new String(conn.getInputStream().readAllBytes());
                    String[] onlineUUIDs = GSON.fromJson(response, String[].class);

                    List<UUID> result = new ArrayList<>();
                    for (String uuidStr : onlineUUIDs) {
                        try {
                            result.add(UUID.fromString(uuidStr));
                        } catch (Exception e) {
                            Client.logger.error("Invalid UUID in filter response: {}", uuidStr);
                        }
                    }

                    return result;
                }

                conn.disconnect();
            } catch (Exception e) {
                Client.logger.error("Failed to filter online users: ", e);
            }

            return new ArrayList<>();
        }, Util.getMainWorkerExecutor());

        try {
            return future.get(3, TimeUnit.SECONDS);
        } catch (Exception e) {
            Client.logger.error("Timeout filtering online users: ", e);
            return new ArrayList<>();
        }
    }

    public static boolean isPlayerOnlineWithCitrus(UUID uuid) {
        if (uuid == null) return false;

        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player != null && client.player.getUuid().equals(uuid)) {
            return isRegistered();
        }

        if (System.currentTimeMillis() - lastOnlineListUpdate >= ONLINE_LIST_CACHE_TIME) {
            updateOnlineUsersList();
        }

        synchronized (ONLINE_CITRUS_USERS) {
            return ONLINE_CITRUS_USERS.contains(uuid);
        }
    }

    public static List<UUID> getCitrusUsersInWorld() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.world == null) return new ArrayList<>();

        List<UUID> result = new ArrayList<>();

        for (net.minecraft.entity.player.PlayerEntity player : client.world.getPlayers()) {
            if (isPlayerOnlineWithCitrus(player.getUuid())) {
                result.add(player.getUuid());
            }
        }

        return result;
    }

    public static boolean shouldSyncWithPlayer(UUID uuid) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player != null && client.player.getUuid().equals(uuid)) {
            return false;
        }

        return isPlayerOnlineWithCitrus(uuid);
    }

    public static void forceUpdateOnlineUsers() {
        updateOnlineUsersList();
    }

    public static boolean isRegistered() {
        return isRegistered;
    }

    public static String getCurrentPlayerName() {
        MinecraftClient client = MinecraftClient.getInstance();

        if (client.player != null) {
            return client.player.getName().getString();
        }

        Session session = client.getSession();
        if (session != null) {
            return session.getUsername();
        }

        return "Unknown";
    }
}