package xyz.tbvns.rogue_block.Managers;

import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.fabric.FabricAdapter;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.session.ClipboardHolder;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.advancement.PlayerAdvancementTracker;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.network.packet.s2c.play.PositionFlag;
import net.minecraft.registry.RegistryEntryLookup;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.entry.RegistryEntryList;
import net.minecraft.registry.entry.RegistryEntryOwner;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandOutput;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.structure.StructureSet;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.Difficulty;
import net.minecraft.world.GameRules;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.dimension.DimensionTypes;
import net.minecraft.world.gen.chunk.FlatChunkGenerator;
import net.minecraft.world.gen.chunk.FlatChunkGeneratorConfig;
import net.minecraft.world.gen.chunk.FlatChunkGeneratorLayer;
import net.minecraft.world.gen.feature.PlacedFeature;
import org.jetbrains.annotations.NotNull;
import xyz.nucleoid.fantasy.Fantasy;
import xyz.nucleoid.fantasy.RuntimeWorldConfig;
import xyz.nucleoid.fantasy.RuntimeWorldHandle;
import xyz.tbvns.rogue_block.Utils;

import java.util.*;
import java.util.stream.Stream;

import static xyz.tbvns.rogue_block.Rogue_block.MOD_ID;

public class WorldManager {

    static RuntimeWorldHandle runWorldHandle;
    static RuntimeWorldHandle lobbyWorldHandle;

    public static void createNewLobbyWorld(MinecraftServer server) {
        RegistryEntryLookup<Biome> biomeLookup = server.getRegistryManager().getOrThrow(RegistryKeys.BIOME);
        RegistryEntryLookup<StructureSet> structureSetLookup = server.getRegistryManager().getOrThrow(RegistryKeys.STRUCTURE_SET);
        RegistryEntryLookup<PlacedFeature> featuresLookup = server.getRegistryManager().getOrThrow(RegistryKeys.PLACED_FEATURE);

        FlatChunkGeneratorConfig config = FlatChunkGeneratorConfig.getDefaultConfig(biomeLookup, structureSetLookup, featuresLookup);
        List<FlatChunkGeneratorLayer> layers = new ArrayList<>() {{
            add(new FlatChunkGeneratorLayer(1, Blocks.BEDROCK)); //The order is reversed
            add(new FlatChunkGeneratorLayer(20, Blocks.STONE));
            add(new FlatChunkGeneratorLayer(7, Blocks.DIRT));
            add(new FlatChunkGeneratorLayer(1, Blocks.GRASS_BLOCK));
        }};
        config = config.with(layers, Optional.of(new StructureSetRegistryEntryList()), biomeLookup.getOrThrow(BiomeKeys.PLAINS));

        FlatChunkGenerator generator = new FlatChunkGenerator(config);

        Fantasy fantasy = Fantasy.get(server);
        RuntimeWorldConfig worldConfig = new RuntimeWorldConfig()
                .setDimensionType(DimensionTypes.OVERWORLD)
                .setDifficulty(Difficulty.HARD)
                .setGameRule(GameRules.DO_DAYLIGHT_CYCLE, false)
                .setGameRule(GameRules.DO_MOB_SPAWNING, false)
                .setTimeOfDay(18000)
                .setGenerator(generator)
                .setFlat(TriState.TRUE)
                .setSeed(0L);

        lobbyWorldHandle = fantasy.getOrOpenPersistentWorld(Identifier.of(MOD_ID, "lobby"), worldConfig);

        ServerWorld world = lobbyWorldHandle.asWorld();
        world.setMobSpawnOptions(false);
        world.setBlockState(new BlockPos(0, 13, 0), Blocks.ACACIA_BUTTON.getDefaultState());

        for (Entity entity : world.iterateEntities()) {
            entity.teleport(world, 0, -999, 0, PositionFlag.VALUES, 0, 0, true);
        }

        ClipboardFormat format = ClipboardFormats.findByAlias("schem");


        try {
            Utils.delay(100, () -> {
                try {
                    ClipboardReader reader = format.getReader(WorldManager.class.getResourceAsStream("/Structures/tower.schem"));
                    Clipboard clipboard = reader.read();
                    EditSession editSession = WorldEdit.getInstance().newEditSession(FabricAdapter.adapt(lobbyWorldHandle.asWorld()));
                    Operation operation = new ClipboardHolder(clipboard)
                            .createPaste(editSession)
                            .to(BlockVector3.at(0, -35, 0))
                            .ignoreAirBlocks(true)
                            .ignoreStructureVoidBlocks(true)
                            .build();
                        Operations.complete(operation);
                    editSession.close();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static void createNewRunWorld(MinecraftServer server) {
        if (runWorldHandle != null) {
            runWorldHandle.delete();
        }

        Fantasy fantasy = Fantasy.get(server);
        RuntimeWorldConfig worldConfig = new RuntimeWorldConfig()
                .setDimensionType(DimensionTypes.OVERWORLD)
                .setDifficulty(Difficulty.HARD)
                .setGameRule(GameRules.DO_DAYLIGHT_CYCLE, true)
                .setGenerator(server.getOverworld().getChunkManager().getChunkGenerator())
                .setSeed(new Random().nextLong());

        runWorldHandle = fantasy.openTemporaryWorld(worldConfig);
    }

    public static void teleportToRunWorld(MinecraftServer server) {
        for (ServerPlayerEntity entity : server.getPlayerManager().getPlayerList()) {
            Random random = new Random();
            int x = random.nextInt(-50, 50);
            int z = random.nextInt(-50, 50);
            entity.teleport(runWorldHandle.asWorld(), x, 255, z, PositionFlag.VALUES, 0, 0, true);
        }
    }

    public static void teleportToLobbyWorld(MinecraftServer server) {
        for (ServerPlayerEntity entity : server.getPlayerManager().getPlayerList()) {
            Random random = new Random();
            int x = random.nextInt(-3, 3);
            int z = random.nextInt(-3, 3);
            entity.teleport(lobbyWorldHandle.asWorld(), x - entity.getX(), 12 - entity.getY(), z - entity.getZ(), PositionFlag.VALUES, 0, 0, true);
        }
    }

    public static void clearAchievements(MinecraftServer server) {
        server.getCommandManager().executeWithPrefix(new ServerCommandSource(
                CommandOutput.DUMMY,
                Vec3d.ZERO,
                Vec2f.ZERO,
                runWorldHandle.asWorld(),
                4,
                "null",
                Text.empty(),
                server,
                null
        ), "advancement revoke @a everything");
    }

    public static void teleportToLobbyWorld(ServerPlayerEntity entity) {
        Random random = new Random();
        int x = random.nextInt(-3, 3);
        int z = random.nextInt(-3, 3);
        entity.teleport(lobbyWorldHandle.asWorld(), x - entity.getX(), 12 - entity.getY(), z - entity.getZ(), PositionFlag.VALUES, 0, 0, true);
    }

    //This is required to remove every structure
    //You should fold this with your code editor
    private static class StructureSetRegistryEntryList implements RegistryEntryList<StructureSet> {
        @Override
        public Stream<RegistryEntry<StructureSet>> stream() {
            return Stream.empty();
        }

        @Override
        public int size() {
            return 0;
        }

        @Override
        public boolean isBound() {
            return false;
        }

        @Override
        public Either<TagKey<StructureSet>, List<RegistryEntry<StructureSet>>> getStorage() {
            return null;
        }

        @Override
        public Optional<RegistryEntry<StructureSet>> getRandom(net.minecraft.util.math.random.Random random) {
            return Optional.empty();
        }

        @Override
        public RegistryEntry<StructureSet> get(int index) {
            return null;
        }

        @Override
        public boolean contains(RegistryEntry<StructureSet> entry) {
            return false;
        }

        @Override
        public boolean ownerEquals(RegistryEntryOwner<StructureSet> owner) {
            return false;
        }

        @Override
        public Optional<TagKey<StructureSet>> getTagKey() {
            return Optional.empty();
        }

        @Override
        public @NotNull Iterator<RegistryEntry<StructureSet>> iterator() {
            return null;
        }
    }
}
