package dev.micle.loginprotection.network;

import dev.micle.loginprotection.LoginProtection;
import dev.micle.loginprotection.network.client.InputPacket;
import dev.micle.loginprotection.network.client.LastInputTickPacket;
import dev.micle.loginprotection.network.server.PlayerStatePacket;
import dev.micle.loginprotection.network.server.RequestLastInputTickPacket;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;

import java.util.regex.Pattern;

public class NetworkManager {
    private static final String VERSION = LoginProtection.MOD_ID + "-net-1";
    private static final Pattern NET_VERSION_PATTERN = Pattern.compile(String.format("^%s-net-\\d+$", LoginProtection.MOD_ID));
    private static final Pattern MOD_VERSION_PATTERN = Pattern.compile("^Forge-\\d+\\.\\d+\\.\\d+-\\d+\\.\\d+\\.\\d+$");
    private static SimpleChannel channel;

    public static void init() {
        // Create channel
        channel = NetworkRegistry.ChannelBuilder.named(LoginProtection.createResourceLocation("network"))
                .clientAcceptedVersions(v -> v.equals(VERSION))
                .serverAcceptedVersions(v -> v.equals(VERSION))
                .networkProtocolVersion(() -> VERSION)
                .simpleChannel();

        // Register packets
        int id = 0;
        channel.messageBuilder(InputPacket.class, id++)
                .encoder(InputPacket::encode)
                .decoder(InputPacket::decode)
                .consumer(InputPacket::handle)
                .add();
        channel.messageBuilder(PlayerStatePacket.class, id++)
                .encoder(PlayerStatePacket::encode)
                .decoder(PlayerStatePacket::decode)
                .consumer(PlayerStatePacket::handle)
                .add();
        channel.messageBuilder(RequestLastInputTickPacket.class, id++)
                .encoder(RequestLastInputTickPacket::encode)
                .decoder(RequestLastInputTickPacket::decode)
                .consumer(RequestLastInputTickPacket::handle)
                .add();
        channel.messageBuilder(LastInputTickPacket.class, id++)
                .encoder(LastInputTickPacket::encode)
                .decoder(LastInputTickPacket::decode)
                .consumer(LastInputTickPacket::handle)
                .add();
    }

    public static void writeVersionInfo(FriendlyByteBuf buffer, boolean senderIsServer) {
        buffer.writeBoolean(senderIsServer);
        buffer.writeUtf(VERSION);
        buffer.writeUtf(LoginProtection.getVersion());
    }

    public static void checkVersion(FriendlyByteBuf buffer) {
        boolean senderIsServer = buffer.readBoolean();
        String serverNetVersion = (senderIsServer) ? readNetVersion(buffer) : VERSION;
        String serverModVersion = (senderIsServer) ? readModVersion(buffer) : LoginProtection.getVersion();
        String clientNetVersion = (senderIsServer) ? VERSION : readNetVersion(buffer);
        String clientModVersion = (senderIsServer) ? LoginProtection.getVersion() : readModVersion(buffer);

        if (!serverNetVersion.equals(clientNetVersion)) {
            throw new MismatchedVersionException(String.format("The server and client are running mismatched " +
                    "versions of [Micle's Login Protection]. Try updating this mod on either the client and or " +
                    "the server. Client version is %s (%s). Server version is %s (%s).", clientModVersion,
                    clientNetVersion, serverModVersion, serverNetVersion));
        }
    }

    public static String readNetVersion(FriendlyByteBuf buffer) {
        String netVersion = buffer.readUtf();
        if (!NET_VERSION_PATTERN.matcher(netVersion).matches()) {
            return String.format("UNKNOWN (%s)", netVersion);
        }
        return netVersion;
    }

    public static String readModVersion(FriendlyByteBuf buffer) {
        String modVersion = buffer.readUtf();
        if (!modVersion.equals("NONE") && !MOD_VERSION_PATTERN.matcher(modVersion).matches()) {
            return String.format("UNKNOWN (%s)", modVersion);
        }
        return modVersion;
    }

    public static SimpleChannel getChannel() {
        return channel;
    }
}
