package de.winniepat.citrus.cosmetics.wings.sync;

import com.google.gson.*;
import de.winniepat.citrus.Client;
import de.winniepat.citrus.managers.ClientRegistrationManager;
import de.winniepat.citrus.utils.SecurityUtils;
import de.winniepat.citrus.cosmetics.wings.ClientWingManager;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Util;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;

public class WingSyncManager {
    private static final String API_BASE_URL = Client.API_URL;
    private static final Gson GSON = new GsonBuilder().create();
    public static final Map<UUID, SyncedWingData> SYNCED_WING_CACHE = new ConcurrentHashMap<>();
    private static final Map<UUID, Long> LAST_SYNC_TIME = new ConcurrentHashMap<>();
    private static final Map<UUID, SyncedWingData> PENDING_UPDATES = new ConcurrentHashMap<>();
    private static final Set<UUID> PENDING_FETCHES = ConcurrentHashMap.newKeySet();

    private static boolean hasSentInitialWings = false;
    private static boolean isInitializing = false;
    private static boolean serverReachable = true;
    private static final long SERVER_CHECK_INTERVAL = 30000;

    private static boolean autoSyncEnabled = true;
    private static long lastAutoSyncTime = 0;
    private static final long AUTO_SYNC_INTERVAL = 5000;

    private static boolean serverHealthy = false;
    private static Timer syncTimer;

    public static class SyncedWingData {
        public String wingType = "none";
        public boolean wingsVisible = true;

        public JsonObject toJson() {
            JsonObject json = new JsonObject();
            json.addProperty("wingType", wingType);
            json.addProperty("wingsVisible", wingsVisible);
            return json;
        }

        public static SyncedWingData fromJson(JsonObject json) {
            SyncedWingData data = new SyncedWingData();
            if (json.has("wingType")) {
                data.wingType = json.get("wingType").getAsString();
            }
            if (json.has("wingsVisible")) {
                data.wingsVisible = json.get("wingsVisible").getAsBoolean();
            }
            return data;
        }

        public static SyncedWingData fromClientWingData(ClientWingManager.WingData clientData) {
            SyncedWingData data = new SyncedWingData();
            data.wingType = clientData.activeWings;
            data.wingsVisible = clientData.wingsVisible;
            return data;
        }

        public ClientWingManager.WingData toClientWingData() {
            ClientWingManager.WingData data = new ClientWingManager.WingData();
            data.activeWings = this.wingType;
            data.wingsVisible = this.wingsVisible;
            return data;
        }
    }

    public static void init() {
        Client.debugLog("Initializing wing sync...");
        isInitializing = true;

        startAutoSync();

        checkServerHealth().thenAccept(healthy -> {
            serverReachable = healthy;
            if (healthy) {
                Client.debugLog("Wing sync server is online");
                scheduleInitialSync();
            } else {
                Client.logger.error("Wing sync server is offline");
                isInitializing = false;
            }
        });
    }

    private static void scheduleInitialSync() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player != null) {
            performInitialSync();
        } else {
            Util.getMainWorkerExecutor().execute(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                performInitialSync();
            });
        }
    }

    private static void performInitialSync() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player == null) {
            isInitializing = false;
            return;
        }

        UUID uuid = client.player.getUuid();
        String playerName = client.player.getName().getString();

        Client.debugLog("Performing initial wing sync for " + playerName + " (" + uuid + ")");

        fetchWingsFromServer(uuid, playerName, true).thenAccept(serverData -> {
            if (serverData != null) {
                Client.debugLog("Server has wings: " + serverData.wingType + ", visible: " + serverData.wingsVisible);
                applyServerWingsToLocal(uuid, serverData);
            } else {
                ClientWingManager.WingData localData = ClientWingManager.getData(uuid);
                if (localData != null && (!"wings".equals(localData.activeWings) || !localData.wingsVisible)) {
                    Client.debugLog("Sending local wings to server: " + localData.activeWings + ", visible: " + localData.wingsVisible);
                    SyncedWingData syncData = SyncedWingData.fromClientWingData(localData);
                    sendWingsToServer(uuid, syncData, playerName, true).thenAccept(success -> {
                        if (success) {
                            Client.debugLog("Initial wing sync completed");
                        }
                    });
                } else {
                    Client.debugLog("No local wing changes to sync");
                }
            }
            hasSentInitialWings = true;
            isInitializing = false;
        });
    }

    public static SyncedWingData getSyncedWings(UUID uuid, String playerName) {
        if (!ClientRegistrationManager.isPlayerOnlineWithCitrus(uuid)) {
            return null;
        }

        if (isInitializing && isLocalPlayer(uuid)) {
            return null;
        }

        if (SYNCED_WING_CACHE.containsKey(uuid)) {
            long lastSync = LAST_SYNC_TIME.getOrDefault(uuid, 0L);
            if (System.currentTimeMillis() - lastSync < 300000) {
                return SYNCED_WING_CACHE.get(uuid);
            }
        }

        if (isLocalPlayer(uuid) && !hasSentInitialWings && !isInitializing) {
            performInitialSync();
            return null;
        }

        if (!PENDING_FETCHES.contains(uuid)) {
            fetchWingsFromServer(uuid, playerName, false);
        }

        return null;
    }

    private static CompletableFuture<SyncedWingData> fetchWingsFromServer(UUID uuid, String playerName, boolean isInitial) {
        PENDING_FETCHES.add(uuid);

        return CompletableFuture.supplyAsync(() -> {
            HttpURLConnection conn = null;
            try {
                String urlString = API_BASE_URL + "/wings/" + uuid.toString();
                Client.debugLog("Fetching wings from: " + urlString);

                URL url = new URI(urlString).toURL();
                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(10000);
                conn.setReadTimeout(10000);

                conn.setRequestProperty("User-Agent", "Minecraft/Citrus");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Accept-Charset", "UTF-8");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());

                int responseCode = conn.getResponseCode();
                Client.debugLog("Wings response code: " + responseCode);

                if (responseCode == 200) {
                    serverReachable = true;
                    try (InputStream is = conn.getInputStream()) {
                        byte[] response = is.readAllBytes();
                        String responseBody = new String(response, StandardCharsets.UTF_8);
                        Client.debugLog("Wings response: " + responseBody);

                        JsonObject json = GSON.fromJson(responseBody, JsonObject.class);

                        SyncedWingData wingData = new SyncedWingData();
                        wingData.wingType = json.get("wingType").getAsString();
                        wingData.wingsVisible = json.get("wingsVisible").getAsBoolean();

                        Client.debugLog("Successfully got wings: " + wingData.wingType + ", visible: " + wingData.wingsVisible);

                        SYNCED_WING_CACHE.put(uuid, wingData);
                        LAST_SYNC_TIME.put(uuid, System.currentTimeMillis());
                        return wingData;
                    }
                } else {
                    serverReachable = false;
                    Client.logger.error("Wing server returned error code {}", responseCode);
                }

            } catch (Exception e) {
                serverReachable = false;
                Client.logger.error("Exception fetching wings: {}", e.getMessage(), e);
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
                PENDING_FETCHES.remove(uuid);
            }
            return null;
        }, Util.getMainWorkerExecutor());
    }

    private static CompletableFuture<Boolean> sendWingsToServer(UUID uuid, SyncedWingData wingData, String playerName, boolean isInitial) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/wings").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("User-Agent", "Citrus/1.0");
                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());
                json.addProperty("wingType", wingData.wingType);
                json.addProperty("wingsVisible", wingData.wingsVisible);
                json.addProperty("playerName", playerName);

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

                int responseCode = conn.getResponseCode();

                if (responseCode == 200) {
                    serverReachable = true;
                    if (isInitial) {
                        hasSentInitialWings = true;
                    }

                    SYNCED_WING_CACHE.put(uuid, wingData);
                    LAST_SYNC_TIME.put(uuid, System.currentTimeMillis());

                    PENDING_UPDATES.remove(uuid);

                    if (!isInitial) {
                        Client.debugLog("Successfully synced wings to server: " + wingData.wingType);
                    }
                    return true;
                } else {
                    serverReachable = false;
                    if (!isInitial) {
                        Client.logger.error("Failed to sync wings to server. HTTP {}", responseCode);
                    }
                    PENDING_UPDATES.put(uuid, wingData);
                    return false;
                }
            } catch (Exception e) {
                serverReachable = false;
                if (!isInitial) {
                    Client.logger.error("Failed to send wings to server", e);
                }
                PENDING_UPDATES.put(uuid, wingData);
                return false;
            }
        }, Util.getMainWorkerExecutor());
    }

    public static void sendWingsToServer(UUID uuid, SyncedWingData wingData, String playerName) {
        sendWingsToServer(uuid, wingData, playerName, false);
    }

    public static void sendWingsToServer(UUID uuid, ClientWingManager.WingData clientWingData, String playerName) {
        SyncedWingData wingData = SyncedWingData.fromClientWingData(clientWingData);
        sendWingsToServer(uuid, wingData, playerName, false);
    }

    public static void sendWingsToServer(UUID uuid, String wingType, boolean wingsVisible, String playerName) {
        SyncedWingData wingData = new SyncedWingData();
        wingData.wingType = wingType;
        wingData.wingsVisible = wingsVisible;
        sendWingsToServer(uuid, wingData, playerName, false);
    }

    private static void applyServerWingsToLocal(UUID uuid, SyncedWingData wingData) {
        MinecraftClient.getInstance().execute(() -> {
            ClientWingManager.WingData localData = ClientWingManager.getData(uuid);
            localData.activeWings = wingData.wingType;
            localData.wingsVisible = wingData.wingsVisible;
            ClientWingManager.save();
            Client.debugLog("Applied server wings: " + wingData.wingType + ", visible: " + wingData.wingsVisible);
        });
    }

    public static void batchFetchWings(List<UUID> uuids) {
        if (uuids.isEmpty()) return;

        List<UUID> citrusUsers = uuids.stream()
                .filter(ClientRegistrationManager::isPlayerOnlineWithCitrus)
                .toList();

        if (citrusUsers.isEmpty()) return;

        CompletableFuture.runAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/wings/batch").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 : uuids) {
                    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) {
                    serverReachable = true;
                    try (InputStream is = conn.getInputStream()) {
                        byte[] response = is.readAllBytes();
                        JsonObject result = GSON.fromJson(new String(response), JsonObject.class);

                        for (String uuidStr : result.keySet()) {
                            try {
                                UUID uuid = UUID.fromString(uuidStr);
                                JsonObject wingDataJson = result.getAsJsonObject(uuidStr);
                                SyncedWingData wingData = SyncedWingData.fromJson(wingDataJson);

                                SYNCED_WING_CACHE.put(uuid, wingData);
                                LAST_SYNC_TIME.put(uuid, System.currentTimeMillis());

                                if (!isLocalPlayer(uuid)) {
                                    ClientWingManager.WingData clientData = wingData.toClientWingData();
                                    ClientWingManager.getData(uuid).activeWings = clientData.activeWings;
                                    ClientWingManager.getData(uuid).wingsVisible = clientData.wingsVisible;
                                }
                            } catch (Exception e) {
                                Client.logger.error("Invalid UUID in wings response: {}", uuidStr, e);
                            }
                        }

                        Client.debugLog("Fetched wings for " + result.size() + " Citrus users");
                    }
                }

                conn.disconnect();
            } catch (Exception e) {
                Client.logger.error("Batch wings fetch failed", e);
            }
        }, Util.getMainWorkerExecutor());
    }

    public static void startAutoSync() {
        if (syncTimer != null) {
            syncTimer.cancel();
        }

        syncTimer = new Timer("WingAutoSync", true);
        syncTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (autoSyncEnabled && System.currentTimeMillis() - lastAutoSyncTime >= AUTO_SYNC_INTERVAL) {
                    performAutoSync();
                    System.out.println("Synced wings");
                }
            }
        }, 2000, AUTO_SYNC_INTERVAL);
    }

    private static void performAutoSync() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player == null || client.world == null) {
            return;
        }

        lastAutoSyncTime = System.currentTimeMillis();

        syncLocalPlayerWings();
        syncNearbyPlayersWings();
        retryPendingUpdates();
    }

    private static void syncLocalPlayerWings() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player == null) return;

        UUID uuid = client.player.getUuid();
        String playerName = client.player.getName().getString();
        ClientWingManager.WingData localData = ClientWingManager.getData(uuid);

        if (localData != null) {
            Long lastSync = LAST_SYNC_TIME.get(uuid);
            if (lastSync == null || System.currentTimeMillis() - lastSync > 30000) {
                SyncedWingData syncData = SyncedWingData.fromClientWingData(localData);
                sendWingsToServer(uuid, syncData, playerName, false);
            }
        }
    }

    private static void syncNearbyPlayersWings() {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.world == null || client.player == null) return;

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

        for (PlayerEntity player : client.world.getPlayers()) {
            if (player == client.player) {
                continue;
            }

            if (ClientRegistrationManager.isPlayerOnlineWithCitrus(player.getUuid())) {
                nearbyCitrusUsers.add(player.getUuid());
            }
        }

        if (!nearbyCitrusUsers.isEmpty()) {
            batchFetchWings(nearbyCitrusUsers);
        }
    }

    static void retryPendingUpdates() {
        if (PENDING_UPDATES.isEmpty()) return;

        MinecraftClient client = MinecraftClient.getInstance();
        if (client.player == null) return;

        for (Map.Entry<UUID, SyncedWingData> entry : PENDING_UPDATES.entrySet()) {
            String playerName = entry.getKey().equals(client.player.getUuid())
                    ? client.player.getName().getString()
                    : entry.getKey().toString();

            sendWingsToServer(entry.getKey(), entry.getValue(), playerName, false);
        }
    }

    public static CompletableFuture<Boolean> checkServerHealth() {
        long lastServerCheck = System.currentTimeMillis();

        return CompletableFuture.supplyAsync(() -> {
            try {
                URL url = new URI(API_BASE_URL + "/health").toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("x-daily-key", SecurityUtils.getDailyKey());
                conn.setConnectTimeout(3000);

                boolean healthy = conn.getResponseCode() == 200;
                serverHealthy = conn.getResponseCode() == 200;
                conn.disconnect();
                return healthy;
            } catch (Exception e) {
                serverHealthy = false;
                return false;
            }
        }, Util.getMainWorkerExecutor());
    }

    private static boolean isLocalPlayer(UUID uuid) {
        MinecraftClient client = MinecraftClient.getInstance();
        return client.player != null && client.player.getUuid().equals(uuid);
    }

    public static void clearCache(UUID uuid) {
        SYNCED_WING_CACHE.remove(uuid);
        LAST_SYNC_TIME.remove(uuid);
    }

    public static void clearAllCache() {
        SYNCED_WING_CACHE.clear();
        LAST_SYNC_TIME.clear();
        PENDING_UPDATES.clear();
        PENDING_FETCHES.clear();
        hasSentInitialWings = false;
        isInitializing = false;
    }

    public static boolean hasInitialSyncCompleted() {
        return hasSentInitialWings && !isInitializing;
    }

    public static boolean isInitializing() {
        return isInitializing;
    }

    public static void forceInitialSync() {
        if (!hasSentInitialWings) {
            isInitializing = true;
            performInitialSync();
        }
    }

    public static boolean isAutoSyncEnabled() {
        return autoSyncEnabled;
    }

    public static void setAutoSyncEnabled(boolean enabled) {
        autoSyncEnabled = enabled;
        if (enabled) {
            startAutoSync();
        } else if (syncTimer != null) {
            syncTimer.cancel();
            syncTimer = null;
        }
    }

    public static String getSyncStatus() {
        if (!autoSyncEnabled) return "Disabled";
        if (isInitializing) return "Initializing...";
        if (hasSentInitialWings) return "Synced";
        return "Not synced";
    }
}

