package tc.oc.commons.bungee.listeners;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;

import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.virtual.ServerDoc.Banner;
import tc.oc.api.model.ModelSync;
import tc.oc.api.servers.ServerStore;
import tc.oc.commons.bungee.servers.LobbyTracker;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.logging.Loggers;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.minecraft.protocol.MinecraftVersion;

import static tc.oc.commons.core.stream.Collectors.toImmutableSet;

@Singleton
public class PingListener implements Listener, PluginFacet {
    private static final int MAX_PLAYERS = 3000;

    private final Logger logger;
    private final Random random = new Random();
    private final Plugin plugin;
    private final Server localServer;
    private final ServerStore serverStore;
    private final ExecutorService executor;
    private final LobbyTracker lobbyTracker;

    @Inject PingListener(Loggers loggers, Plugin plugin, Server localServer, ServerStore serverStore, LobbyTracker lobbyTracker, @ModelSync ExecutorService executor) {
        this.plugin = plugin;
        this.localServer = localServer;
        this.serverStore = serverStore;
        this.executor = executor;
        this.lobbyTracker = lobbyTracker;
        this.logger = loggers.get(getClass());
    }

    private Banner chooseBanner() {
        List<Banner> banners = localServer.banners();
        if(banners.isEmpty()) return null;

        int totalWeight = 0;
        for(Banner banner : banners) totalWeight += banner.weight();
        int rando = random.nextInt(totalWeight);

        for(Banner banner : banners) {
            rando -= banner.weight();
            if(rando < 0) return banner;
        }

        return null;
    }


    @EventHandler
    public void onPing(final ProxyPingEvent event) {
        event.registerIntent(plugin);

        final int proto = event.getConnection().getVersion();
        final Set<Integer> supported = lobbyTracker.supportedProtocols().collect(toImmutableSet());

        if(!supported.isEmpty() && !supported.contains(proto)) {
            event.getResponse().setVersion(new ServerPing.Protocol(
                new Component("Connect with ", ChatColor.RED)
                    .extra(describeVersionRange(supported))
                    .toLegacyText(),
                -1
            ));
        }

        executor.execute(() -> {
            event.getResponse().setPlayers(new ServerPing.Players(MAX_PLAYERS, serverStore.countBukkitPlayers(), null));
            final Banner banner = chooseBanner();
            if(banner != null) event.getResponse().setDescription(banner.rendered());
            event.completeIntent(plugin);
        });
    }

    public static BaseComponent describeVersionRange(Collection<Integer> protos) {
        final Set<MinecraftVersion> versions = protos.stream()
                                                     .map(MinecraftVersion::byProtocol)
                                                     .filter(v -> v != null)
                                                     .collect(toImmutableSet());
        final MinecraftVersion oldest = Collections.min(versions);
        final MinecraftVersion newest = Collections.max(versions);
        final Component c = new Component(oldest.version(), ChatColor.AQUA);
        if(oldest != newest) {
            c.extra(" to ", ChatColor.RED)
             .extra(newest.version());
        }
        return c;
    }
}
