/*
 *   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.tablist;

import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.api.Disable;
import de.steamwar.network.packets.common.FightInfoPacket;
import de.steamwar.persistent.Storage;
import de.steamwar.persistent.Subserver;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.listeners.BasicListener;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Linked
public class TablistManager extends BasicListener implements Disable {

    private static final Map<RegisteredServer, FightInfoPacket> fightInfos = new HashMap<>();

    public static synchronized void newFightInfo(RegisteredServer info, FightInfoPacket packet) {
        fightInfos.put(info, packet);
        fightInfos.keySet().removeIf(serverInfo -> serverInfo.getPlayersConnected().isEmpty());
    }

    private final Map<Player, Tablist> tablists = new HashMap<>();

    private int seconds = 0;

    public TablistManager() {
        VelocityCore.schedule(this::updateTablist).repeat(1, TimeUnit.SECONDS).schedule();
        synchronized (tablists) {
            VelocityCore.getProxy().getAllPlayers().forEach(player -> tablists.computeIfAbsent(player, Tablist::new).onServerSwitch());
        }
    }

    @Subscribe
    public void onServerConnection(ServerConnectedEvent event) {
        synchronized (tablists) {
            tablists.computeIfAbsent(event.getPlayer(), Tablist::new).onServerSwitch();
        }

        if (event.getPlayer().getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20)) {
            Tablist.sendPacket(event.getPlayer(), Tablist.createTeamPacket);
        }
    }

    @Subscribe
    public void onServerPostConnection(ServerPostConnectEvent event) {
        synchronized (tablists) {
            tablists.get(event.getPlayer()).onServerPostSwitch();
        }
    }

    @Subscribe
    public void onLeave(DisconnectEvent event) {
        synchronized (tablists) {
            tablists.remove(event.getPlayer());
            Storage.directTabItems.remove(event.getPlayer());
        }
    }

    public void disable() {
        synchronized (tablists) {
            tablists.forEach((player, tablist) -> tablist.disable());
            tablists.clear();
        }
    }

    private void updateTablist() {
        List<TablistPart> subservers = new ArrayList<>();
        for (RegisteredServer server : new ArrayList<>(VelocityCore.getProxy().getAllServers())) {
            if (server.getPlayersConnected().isEmpty()) continue;

            Subserver subserver = Subserver.getSubserver(server.getServerInfo());
            if (fightInfos.containsKey(server)) {
                subservers.add(new TablistServer(server, fightInfos.get(server)));
            } else if (!Subserver.isBuild(subserver)) {
                subservers.add(new TablistServer(server));
            }
        }
        subservers.add(new TablistBuild());
        subservers.sort((s1, s2) -> s1.sortKey().compareToIgnoreCase(s2.sortKey()));
        TablistPart global = new TablistGroup(subservers);

        synchronized (tablists) {
            tablists.forEach((player, tablist) -> tablist.update(global, seconds));
        }
        seconds++;
    }
}
