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

import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.PointsWinScore;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import io.github.togar2.pvp.events.EntityKnockbackEvent;
import io.github.togar2.pvp.events.FinalAttackEvent;
import io.github.togar2.pvp.events.PrepareAttackEvent;
import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.instance.InstanceTickEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.block.Block;
import org.jetbrains.annotations.NotNull;

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

class HighGround extends StatelessGame {
    private final int radius;
    private final int seconds;
    private final WeakHashMap<Player, Integer> scoreMap = new WeakHashMap<>();

    HighGround(int radius, int seconds) {
        super(Dimension.THE_END.key, "highground", new PointsWinScore());
        this.radius = radius;
        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 -> finalAttackEvent.setBaseDamage(0)
        );

        this.eventNode().addListener(PrepareAttackEvent.class, prepareAttackEvent -> {
            if(this.isBeforeBeginning){
                prepareAttackEvent.setCancelled(true);
            }
        });

        this.eventNode().addListener(
            EntityKnockbackEvent.class,
            entityKnockbackEvent -> entityKnockbackEvent.setStrength(1.1f)
        );

        this.eventNode().addListener(InstanceTickEvent.class, instanceTickEvent ->  {
            if (this.isBeforeBeginning || !this.isRunning) return;
            this.getPlayers().forEach(player -> {
                if((player.isOnGround() && player.getPosition().y() >= 1) || (!player.isOnGround() && player.getPosition().y() >= 1.5)){
                    this.scoreMap.put(player, this.scoreMap.get(player) + 1);
                    player.setLevel(this.scoreMap.get(player) / 20);
                    player.setExp((this.scoreMap.get(player) % 20) / 20.0f);
                }
            });
        });
    }

    @Override
    protected void onLoad(@NotNull CompletableFuture<Void> callback) {
        for (int y = 0; y >= -3; y--) {
            int radius = (Math.abs(y) * 5) + this.radius;
            for (int x = -radius; x <= radius; x++) {
                for (int z = -radius; z <= radius; z++) {
                    double distance = new Pos(x, 0, z).distance(0, 0, 0);
                    if (distance <= radius) {
                        this.setBlock(x, y, z, y == 0 ? Block.DIAMOND_BLOCK : Block.GRASS_BLOCK);
                        Pos featurePosition = new Pos(x, y + 1, z);

                        if(y >= 0 || this.getBlock(featurePosition).isSolid()) continue;
                        if (this.rnd.nextDouble() < 0.1){
                            this.setBlock(featurePosition, Block.SHORT_GRASS);
                        }
                        if (this.rnd.nextDouble() < 0.01){
                            this.setBlock(featurePosition, BlockPallet.FLOWER.rnd());
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
        Player player = playerMoveEvent.getPlayer();
        if(playerMoveEvent.getNewPosition().y() < -10){
            player.teleport(this.getSpawn());
        }
    }

    @Override
    protected void start() {
        this.getPlayers().forEach(player -> this.scoreMap.put(player, 0));
        super.start();
    }

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

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

    @Override
    public Pos getSpawn() {
        double theta = this.rnd.nextDouble() * 2 * Math.PI;

        double spawnRadius = this.radius + 5;
        double x = spawnRadius * Math.cos(theta);
        double z = spawnRadius * Math.sin(theta);

        return new Pos(x, 0, z).withLookAt(new Pos(0, 0, 0));
    }
}
