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

import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import eu.mhsl.minenet.minigames.score.LastWinsScore;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import eu.mhsl.minenet.minigames.util.GeneratorUtils;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.*;
import net.minestom.server.entity.metadata.other.PrimedTntMeta;
import net.minestom.server.event.EventListener;
import net.minestom.server.event.entity.projectile.ProjectileCollideWithBlockEvent;
import net.minestom.server.event.entity.projectile.ProjectileCollideWithEntityEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.tag.Tag;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.CompletableFuture;

public class BowSpleef extends StatelessGame {
    private static final Tag<Long> CHARGE_BOW_SINCE = Tag.Long("CHARGE_BOW_SINCE");
    private static final Tag<Boolean> ARROW_FIRST_HIT = Tag.Boolean("ARROW_ALREADY_LIT");
    private final int radius = 30;
    private final int totalElevation = 50;
    public BowSpleef() {
        super(Dimension.OVERWORLD.key, "bowSpleef", new LastWinsScore());

//        eventNode().addListener( TODO implement bow mechanism
//                EventListener
//                        .builder(PlayerItemAnimationEvent.class)
//                        .handler(playerItemAnimationEvent -> playerItemAnimationEvent.getPlayer().setTag(CHARGE_BOW_SINCE, System.currentTimeMillis()))
//                        .filter(playerItemAnimationEvent -> playerItemAnimationEvent.getItemAnimationType() == PlayerItemAnimationEvent.ItemAnimationType.BOW)
//                        .build()
//        );
//
//        eventNode().addListener(
//                EventListener
//                        .builder(ItemUpdateStateEvent.class)
//                        .handler(event -> {
//                            final Player player = event.getPlayer();
//                            final double chargedFor = (System.currentTimeMillis() - player.getTag(CHARGE_BOW_SINCE)) / 1000D;
//                            final double power = MathUtils.clamp((chargedFor * chargedFor + 2 * chargedFor) / 2D, 0, 1);
//
//                            if (power > 0.2) {
//                                final CustomEntityProjectile projectile = new CustomEntityProjectile(player, EntityType.ARROW);
//                                final ArrowMeta meta = (ArrowMeta) projectile.getEntityMeta();
//                                meta.setCritical(power >= 0.9);
//                                projectile.scheduleRemove(Duration.of(100, TimeUnit.SERVER_TICK));
//                                meta.setOnFire(true);
//
//                                final Pos position = player.getPosition().add(0, player.getEyeHeight(), 0);
//                                projectile.setInstance(Objects.requireNonNull(player.getInstance()), position);
//
//                                final Vec direction = projectile.getPosition().direction();
//                                projectile.shootFrom(position.add(direction).sub(0, 0.2, 0), power * 3, 1.0);
//                                projectile.setTag(ARROW_FIRST_HIT, true);
//                            }
//                        })
//                        .filter(itemUpdateStateEvent -> itemUpdateStateEvent.getItemStack().material() == Material.BOW)
//                        .build()
//        );

        eventNode().addListener(
                EventListener
                        .builder(ProjectileCollideWithBlockEvent.class)
                        .handler(projectileBlockHitEvent -> {
                            Entity projectile = projectileBlockHitEvent.getEntity();
                            if(!projectile.getTag(ARROW_FIRST_HIT)) {
                                projectile.remove();
                                return;
                            }

                            float radius = 0.5F;
                            Point arrowPos = projectile.getPosition();
                            GeneratorUtils.foreachXZ(arrowPos.add(radius), arrowPos.sub(radius), point -> {
                                Point location = point.add(projectile.getVelocity().mul(0.04)).withY(totalElevation);
                                if(!getBlock(location).isAir()) {
                                    setBlock(location, Block.AIR);

                                    Entity fallingTnt = new Entity(EntityType.TNT);
                                    PrimedTntMeta fallingTntMeta = (PrimedTntMeta) fallingTnt.getEntityMeta();
                                    fallingTntMeta.setFuseTime(20);
                                    fallingTnt.setInstance(this, location);
                                    fallingTnt.setVelocity(new Vec(0, 3, 0));
                                }
                            });

                            projectile.setTag(ARROW_FIRST_HIT, false);
                        })
                        .build()
        );

        eventNode().addListener(
                EventListener
                        .builder(ProjectileCollideWithEntityEvent.class)
                        .handler(projectileEntityHitEvent -> projectileEntityHitEvent.setCancelled(true))
                        .build()
        );

    }

    @Override
    protected void onLoad(@NotNull CompletableFuture<Void> callback) {
        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
        for(int x = -radius; x <= radius; x++) {
            for(int z = -radius; z <= radius; z++) {
                if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) continue;

                batch.setBlock(x, totalElevation, z, Block.TNT);
            }
        }
        BatchUtil.loadAndApplyBatch(batch, this, () -> callback.complete(null));
    }

    @Override
    protected void onStart() {
        getPlayers().forEach(player -> {
            player.getInventory().setItemStack(
                    0,
                    ItemStack
                            .builder(Material.BOW)
                            .customName(TranslatedComponent.byId("bow").getAssembled(player))
                            .glowing(true)
                            .build()
            );
        });
    }

    @Override
    protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
        if(playerMoveEvent.getNewPosition().y() < totalElevation) {
            getScore().insertResult(playerMoveEvent.getPlayer());
            playerMoveEvent.getPlayer().setGameMode(GameMode.SPECTATOR);
        }
    }

    @Override
    public Pos getSpawn() {
        return new Pos(0, totalElevation + 1, 0);
    }
}
