/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.velocitycore.network.handlers;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.util.GameProfile;
import de.steamwar.linkage.Linked;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.common.PlayerSkinRequestPacket;
import de.steamwar.network.packets.common.PlayerSkinResponsePacket;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.network.NetworkSender;
import de.steamwar.velocitycore.network.ServerMetaInfo;
import lombok.SneakyThrows;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

@Linked
public class PlayerSkinHandler extends PacketHandler {

    private final int maxCacheSize = 1000;

    public PlayerSkinHandler() {
        VelocityCore.getProxy().getEventManager().register(VelocityCore.get(), this);
    }

    private final Map<UUID, SkinData> skins = new LinkedHashMap<>(maxCacheSize, 1, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<UUID, SkinData> eldest) {
            return size() > maxCacheSize;
        }
    };

    @Handler
    @SneakyThrows
    public void handle(PlayerSkinRequestPacket packet) {
        synchronized (skins) {
            if (skins.containsKey(packet.getUuid())) {
                SkinData skinData = skins.get(packet.getUuid());
                NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skinData.skin, skinData.signature));
                return;
            }

            String url = "https://sessionserver.mojang.com/session/minecraft/profile/" + packet.getUuid().toString().replace("-", "") + "?unsigned=false";

            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setRequestProperty("User-Agent", "SkinFetcher");

            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return;
            }

            InputStream is = connection.getInputStream();
            String json = new BufferedReader(new InputStreamReader(is))
                    .lines().collect(Collectors.joining("\n"));

            JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
            JsonArray properties = obj.getAsJsonArray("properties");
            for (JsonElement propElement : properties) {
                JsonObject prop = propElement.getAsJsonObject();
                if (prop.get("name").getAsString().equals("textures")) {
                    String skin = prop.get("value").getAsString();
                    String signature = prop.get("signature").getAsString();
                    skins.put(packet.getUuid(), new SkinData(skin, signature));
                    NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skin, signature));
                    return;
                }
            }
        }
    }

    @Subscribe
    public void onPostLogin(PostLoginEvent event) {
        Player player = event.getPlayer();
        GameProfile gameProfile = player.getGameProfile();
        GameProfile.Property property = gameProfile.getProperties().stream().filter(p -> p.getName().equals("textures")).findFirst().orElse(null);
        if (property == null) return;

        synchronized (skins) {
            skins.put(player.getUniqueId(), new SkinData(property.getValue(), property.getSignature()));

            Set<UUID> uuidSet = skins.keySet();
            VelocityCore.getProxy().getAllServers().forEach(server -> {
                for (UUID uuid : uuidSet) {
                    NetworkSender.send(server, new PlayerSkinResponsePacket(uuid, property.getValue(), property.getSignature()));
                }
            });
        }
    }

    public record SkinData(String skin, String signature) {
    }
}
