package com.livingworld.commands;

import com.livingworld.core.LivingWorldConstants;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.levelgen.Heightmap;
import org.joml.Vector3f;

/**
 * Implements {@code /lw region borders [regionX regionZ]}.
 *
 * <p>Draws temporary cyan dust-particle lines along all four edges of the
 * specified region (or the player's current region if no coordinates are given).
 * Each edge is sampled at the world surface so borders follow terrain.</p>
 */
public final class RegionBordersCommand {

    private static final int REGION_BLOCKS = LivingWorldConstants.DEFAULT_REGION_SIZE_CHUNKS * 16;
    private static final int EDGE_SAMPLES  = 32; // particles per edge (~4 blocks apart on 128-block edge)

    // Cyan-teal colour for region borders.
    private static final DustParticleOptions BORDER_PARTICLE =
            new DustParticleOptions(new Vector3f(0.0f, 0.95f, 0.85f), 1.5f);

    private RegionBordersCommand() {}

    public static LiteralArgumentBuilder<CommandSourceStack> build() {
        return Commands.literal("borders")
                .executes(ctx -> execute(ctx.getSource(), null, null))
                .then(Commands.argument("regionX", IntegerArgumentType.integer())
                        .then(Commands.argument("regionZ", IntegerArgumentType.integer())
                                .executes(ctx -> execute(
                                        ctx.getSource(),
                                        IntegerArgumentType.getInteger(ctx, "regionX"),
                                        IntegerArgumentType.getInteger(ctx, "regionZ")))));
    }

    private static int execute(CommandSourceStack source, Integer regionX, Integer regionZ) {
        ServerPlayer player;
        try {
            player = source.getPlayerOrException();
        } catch (CommandSyntaxException e) {
            source.sendFailure(Component.literal("/lw region borders requires a player"));
            return 0;
        }

        int rx = regionX != null ? regionX
                : (int) Math.floor(player.getX() / REGION_BLOCKS);
        int rz = regionZ != null ? regionZ
                : (int) Math.floor(player.getZ() / REGION_BLOCKS);

        ServerLevel level = player.serverLevel();
        int baseX = rx * REGION_BLOCKS;
        int baseZ = rz * REGION_BLOCKS;

        for (int i = 0; i <= EDGE_SAMPLES; i++) {
            int offset = (int) ((double) i / EDGE_SAMPLES * REGION_BLOCKS);
            // North edge: z = baseZ, vary x
            spawnEdgeParticles(level, baseX + offset, baseZ);
            // South edge: z = baseZ + REGION_BLOCKS, vary x
            spawnEdgeParticles(level, baseX + offset, baseZ + REGION_BLOCKS);
            // West edge: x = baseX, vary z
            spawnEdgeParticles(level, baseX, baseZ + offset);
            // East edge: x = baseX + REGION_BLOCKS, vary z
            spawnEdgeParticles(level, baseX + REGION_BLOCKS, baseZ + offset);
        }

        source.sendSuccess(() -> Component.literal(String.format(
                "[LW] Region (%d,%d) borders — blocks X[%d..%d] Z[%d..%d]",
                rx, rz, baseX, baseX + REGION_BLOCKS - 1, baseZ, baseZ + REGION_BLOCKS - 1)),
                false);
        return 1;
    }

    private static void spawnEdgeParticles(ServerLevel level, int x, int z) {
        int y = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z) + 1;
        // Use count=3 so the vertical column is visible even in high terrain.
        level.sendParticles(BORDER_PARTICLE,
                x + 0.5, y + 0.5, z + 0.5,
                3,          // count
                0.0, 0.8, 0.0,  // spread x/y/z
                0.0);       // speed (0 = stationary)
    }
}
