package ru.dragonestia.kubik.plugin;

import com.google.gson.Gson;
import com.google.inject.Inject;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.slf4j.Logger;
import ru.dragonestia.kubik.client.Kubik;
import ru.dragonestia.kubik.client.proxy.ProxyControl;
import ru.dragonestia.kubik.client.proxy.ProxyServerRegistry;
import ru.dragonestia.kubik.client.util.EnvUtil;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

@Plugin(
    id = "kubik-velocity-plugin",
    name = "KubikConnector",
    version = "1.0.0",
    description = "Kubik connector plugin",
    authors = { "ScarletRedMan" }
)
public class KubikPlugin implements ProxyServerRegistry {

    private final static MinecraftChannelIdentifier KUBIK_TRANSFER_CHANNEL_ID = MinecraftChannelIdentifier.from("kubik:transfer");

    private final ProxyServer proxyServer;
    private final Logger logger;
    private final ProxyControl proxyControl;

    private final Gson gson = new Gson();
    private final Map<String, Consumer<PluginMessageEvent>> pluginChannelHandlers = new ConcurrentHashMap<>();
    private final Map<String, RegisteredServer> registeredServers = new ConcurrentHashMap<>();

    private Kubik kubik;
    private String initialServerGroupId;

    @Inject
    public KubikPlugin(ProxyServer proxyServer, Logger logger) {
        this.proxyServer = proxyServer;
        this.logger = logger;

        var kubikHttpUrl = EnvUtil.getString("KUBIK_DISCOVERY_HTTP_URL", "http://localhost:8080");
        var kubikWsUrl = EnvUtil.getString("KUBIK_DISCOVERY_WS_URL", "ws://localhost:8080");

        logger.info("Using kubik urls: {}, {}", kubikHttpUrl, kubikWsUrl);

        kubik = new Kubik(kubikHttpUrl, kubikWsUrl);
        proxyControl = kubik.initProxy(this);

        initialServerGroupId = EnvUtil.getString("KUBIK_INITIAL_SERVER_GROUP_ID", "lobby");
    }

    @Subscribe
    public void onProxyInitialization(ProxyInitializeEvent event) {
        registerPluginChannel(KUBIK_TRANSFER_CHANNEL_ID, e -> {
            e.setResult(PluginMessageEvent.ForwardResult.handled());

            if (!(e.getSource() instanceof ServerConnection backend)) return;
            var serverId = new String(e.getData(), StandardCharsets.UTF_8);
            var player = (Player) e.getTarget();

            logger.info("Taken transfer request for player {} to {}", player.getUsername(), serverId);
            var server = registeredServers.get(serverId);
            if (server == null) {
                logger.error("Could not find server with id '{}'", serverId);
                player.disconnect(Component.text("Сервер '{}' недоступен", NamedTextColor.RED));
                return;
            }

            player.createConnectionRequest(server).fireAndForget();
        });
    }

    @Subscribe
    public void onPreShutdown(ProxyShutdownEvent event) {
        proxyControl.dispose();
    }

    @Subscribe
    public void onChooseInitialServer(PlayerChooseInitialServerEvent event) {
        var player = event.getPlayer();

        if (initialServerGroupId == null) {
            logger.error("Initial server group id is null");
            player.disconnect(Component.text("Неправильно сконфигурировано лобби сервера"));
            return;
        }

        var serverId = kubik.pickServer(initialServerGroupId);
        if (serverId == null) {
            logger.error("Could not find server with groupId '{}'", initialServerGroupId);
            player.disconnect(Component.text("Нет доступных серверов для лобби"));
            return;
        }

        var server = registeredServers.get(serverId);
        if (server == null) {
            logger.error("Server '{}' is not registered for groupId '{}'", serverId, initialServerGroupId);
            player.disconnect(Component.text("Не зарегистрирован сервер для лобби"));
            return;
        }

        event.setInitialServer(server);
    }

    @Subscribe
    public void onTakenPluginMessage(PluginMessageEvent event) {
        var handler = pluginChannelHandlers.get(event.getIdentifier().getId());
        if (handler != null) handler.accept(event);
    }

    private void registerPluginChannel(MinecraftChannelIdentifier channelId, Consumer<PluginMessageEvent> handler) {
        pluginChannelHandlers.put(channelId.getId(), handler);
        proxyServer.getChannelRegistrar().register(KUBIK_TRANSFER_CHANNEL_ID);
        logger.info("Registered plugin channel '{}'", channelId.getId());
    }

    @Override
    public void register(String id, String address, int port) {
        var server = registeredServers.computeIfAbsent(id, _ -> proxyServer.createRawRegisteredServer(new ServerInfo(
                id,
                InetSocketAddress.createUnresolved(address, port)
        )));

        logger.info("Registered server '{}'(address={} port={})", id, address, port);
    }

    @Override
    public void unregister(String id) {
        var server = registeredServers.remove(id);

        logger.info("Unregistered server '{}'", id);

        if (server == null) return;

        //TODO
    }
}
