package eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight;

import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.LowestPointsWinScore;
import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
import io.github.togar2.pvp.events.FinalAttackEvent;
import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;

public class Stickfight extends StatelessGame {
    private final double radius;
    private final WeakHashMap<Player, Pos> spawnPoints = new WeakHashMap<>();
    private final Map<Player, Integer> scoreMap = new WeakHashMap<>();
    private boolean countdownStarted = false;
    private final int seconds;

    public Stickfight(int length, int seconds) {
        super(Dimension.OVERWORLD.key, "Stickfight", new LowestPointsWinScore());
        this.radius = length;
        this.seconds = seconds;

        this.eventNode().addChild(
            CombatFeatures.empty()
                .add(CombatFeatures.VANILLA_ATTACK)
                .add(CombatFeatures.VANILLA_DAMAGE)
                .add(CombatFeatures.VANILLA_KNOCKBACK)
                .build().createNode()
        );

        this.eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> {
            if(this.isBeforeBeginning) finalAttackEvent.setCancelled(true);
            finalAttackEvent.setBaseDamage(0);
            ((Player) finalAttackEvent.getTarget()).setHealth(20);
        });

        this.setGenerator(new CircularPlateTerrainGenerator(20));
    }

    @Override
    protected void onLoad(@NotNull CompletableFuture<Void> callback) {
        this.replaceCircle(Block.SANDSTONE);
    }

    private void replaceCircle(Block block) {
        int radius = 8;
        for(int x = -radius; x <= radius; x++) {
            for(int z = -radius; z <= radius; z++) {
                Pos blockPosition = this.getSpawn().add(x, -1, z);
                if(blockPosition.distance(this.getSpawn().sub(0, 1, 0)) <= radius) this.setBlock(blockPosition, block);
            }
        }
    }

    @Override
    protected void start() {
        List<Player> players = this.getPlayers().stream().toList();
        int numPlayers = players.size();
        this.countdownStarted = true;

        this.replaceCircle(Block.AIR);
        for(int i = 0; i < numPlayers; i++) {
            double angle = (2 * Math.PI / numPlayers) * i;
            int spawnX = (int) (this.radius * Math.cos(angle));
            int spawnZ = (int) (this.radius * Math.sin(angle));
            int spawnY = 50;

            Pos spawnpoint = new Pos(spawnX, spawnY + 1, spawnZ).add(0.5);
            this.spawnPoints.put(players.get(i), spawnpoint.withLookAt(this.getSpawn()));
            players.get(i).teleport(spawnpoint);

            this.generateBridge(spawnX, spawnY, spawnZ);
        }

        ItemStack item = ItemStack.of(Material.STICK).withGlowing(true);
        players.forEach(player -> player.getInventory().addItemStack(item));

        this.setBlock(0, 50, 0, Block.GOLD_BLOCK);
        super.start();
    }

    @Override
    protected void onStart() {
        this.setTimeLimit(this.seconds);
    }

    @Override
    protected void onStop() {
        this.scoreMap.forEach((player, score) -> this.getScore().insertResult(player, score));
    }

    private void generateBridge(int startX, int startY, int startZ) {
        int steps = (int) (this.radius * 1.5);
        for(int i = 0; i < steps; i++) {
            double t = (double) i / steps;
            int x = (int) (startX * (1 - t));
            int z = (int) (startZ * (1 - t));
            this.setBlock(x, startY, z, Block.SANDSTONE);
        }
    }

    @Override
    protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
        Player player = playerMoveEvent.getPlayer();
        if(!this.spawnPoints.containsKey(player)) {
            if(playerMoveEvent.getNewPosition().y() < 45) player.teleport(this.getSpawn());
            if(this.countdownStarted) playerMoveEvent.setCancelled(true);
            return;
        }

        if(this.isBeforeBeginning) {
            if(this.spawnPoints.get(player).distance(playerMoveEvent.getNewPosition()) < 1) return;
            playerMoveEvent.setCancelled(true);
            player.teleport(this.spawnPoints.get(player));
        }

        if(playerMoveEvent.getNewPosition().y() < 40) {
            player.teleport(this.spawnPoints.get(player));
            this.scoreMap.putIfAbsent(player, 0);
            this.scoreMap.put(player, this.scoreMap.get(player) + 1);
            player.setLevel(this.scoreMap.get(player));
        }
    }

    @Override
    public Pos getSpawn() {
        return new Pos(0.5, 51, 0.5);
    }
}
