package xyz.illuc.neuralgiacore.events;

import net.adinvas.prototype_pain.PlayerHealthProvider;
import net.adinvas.prototype_pain.compat.prototype_physics.PhysicsUtil;
import net.adinvas.prototype_pain.limbs.Limb;
import net.adinvas.prototype_physics.JbulletWorld;
import net.adinvas.prototype_physics.PlayerPhysics;
import net.adinvas.prototype_physics.RagdollPart;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import xyz.illuc.neuralgiacore.mixin.PrototypePhysics.MixinPhysicsAccessor;

import java.util.Random;

@Mod.EventBusSubscriber
public class DamageTypeEvent {
    @SubscribeEvent(priority = EventPriority.HIGHEST)
    public static void onLivingDamage(LivingHurtEvent event){
        if (!(event.getEntity() instanceof Player player))return;

        // Push ragdolled entity on receiving damage
        if (event.getSource().getEntity() != null) {
            if (player instanceof ServerPlayer serverPlayer){
                if (!player.level().isClientSide && PhysicsUtil.isPhysicsActivated(player)) {
                    player.getCapability(PlayerHealthProvider.PLAYER_HEALTH_DATA, null).ifPresent(cap-> {
                        if (cap.getContiousness() < 20) {
                            double dx = player.getX() - event.getSource().getEntity().getX();
                            double dy = player.getY() - event.getSource().getEntity().getY();
                            double dz = player.getZ() - event.getSource().getEntity().getZ();


                            double strength = 15F;



                            JbulletWorld world = JbulletWorld.get((ServerLevel) player.level());

                            PlayerPhysics physics = world.getPlayerPhys(serverPlayer);
                            Vec3 targetVelocity = new Vec3((float) (dx * strength), (float) (dy * strength), (float) (dz * strength));
                            //System.out.println(physics.getMode().name());

                            MixinPhysicsAccessor accessor = (MixinPhysicsAccessor) physics;
                            accessor.getRagdollParts().forEach(part -> {
                                part.activate();
                            });
                            //physics.forceMode(PlayerPhysics.Mode.PRECISE);
                            //world.addPlayer(target);

                            //physics.applyVelocityToWhole(targetVelocity);


                            if (((MixinPhysicsAccessor) physics).getRagdollParts().contains(RagdollPart.TORSO))
                                physics.applyVel(RagdollPart.TORSO, targetVelocity);


                            player.hurtMarked = true; // force motion sync

                        }
                    });
                }
            }
        }


        //System.out.println(event.getSource().toString()); //inFire onFire hotFloor
        if (event.getSource().getEntity() != null) {
            // Collapse damage
            String entity = event.getSource().getEntity().getType().toString();

            if (entity.equals("entity.tfc.falling_block")) {
                String type = event.getSource().getEntity().serializeNBT().getCompound("BlockState").getString("Name");
                if (type.contains("tfc:rock")) {
                    player.getCapability(PlayerHealthProvider.PLAYER_HEALTH_DATA).ifPresent(h->{
                        h.handleBluntDamage(10, player, pickRandomByWeight());
                        h.handleBluntDamage(10, player, pickRandomByWeight());
                        h.handleBluntDamage(10, player, pickRandomByWeight());
                        h.handleBluntDamage(10, player, pickRandomByWeight());
                        h.handleBluntDamage(10, player, pickRandomByWeight());

                        h.setContiousness(h.getContiousness() - 60);

                        h.setLimbDislocation(pickRandomByWeight(), 80);
                        h.setLimbDislocation(pickRandomByWeight(), 80);
                    });

                } else {
                    player.getCapability(PlayerHealthProvider.PLAYER_HEALTH_DATA).ifPresent(h->{
                        h.handleBluntDamage(1, player, Limb.HEAD);
                    });
                }

            } else if (entity.equals("entity.tfc.wolf") || entity.equals("entity.tfc.direwolf")) {
                Limb targetLimb = pickRandomByWeight();
                Bite(player, targetLimb, 1F);
            } else if (entity.equals("entity.tfc.black_bear") || entity.equals("entity.tfc.grizzly_bear")) {
                Limb targetLimb = pickRandomByWeight();
                Bite(player, targetLimb, 2F, true, true);
            }

        // should prolly change this to not use strings at some point
        } else if (event.getSource().toString().equals("DamageSource (inWall)")) {
            player.getCapability(PlayerHealthProvider.PLAYER_HEALTH_DATA).ifPresent(h->{
                h.setOxygen(h.getOxygen() - 10);
            });
            event.setAmount(0);
        } else if (event.getSource().toString().equals("DamageSource (hotFloor)")) {

            Limb target = pickRandomBottomByWeight();

            Burn(player, target, event.getAmount());

            event.setAmount(0);
        } else if (event.getSource().toString().equals("DamageSource (onFire)")) {

            Limb target = pickRandomByWeight();

            Burn(player, target, event.getAmount()*2);

            event.setAmount(0);
        } else if (event.getSource().toString().equals("DamageSource (inFire)")) {

            Limb target = pickRandomBottomByWeight();

            Burn(player, target, event.getAmount()*4);

            event.setAmount(0);
        }


    }

    // helper functions

    public static void Burn(Player player, Limb target, Float damage) {
        player.getCapability(PlayerHealthProvider.PLAYER_HEALTH_DATA).ifPresent(h->{
            if (h.getLimbSkinHealth(target) <= 1)
                h.applyMuscleDamage(target, damage * 2, player);
            else
                h.applySkinDamage(target, damage * 2);
            h.applyPain(target, damage*10);
        });
    }

    public static RagdollPart pickRandomRagdollPartByWeight() {
        RagdollPart[] limb_list = new RagdollPart[]{RagdollPart.HEAD, RagdollPart.TORSO, RagdollPart.TORSO, RagdollPart.RIGHT_ARM, RagdollPart.RIGHT_ARM, RagdollPart.RIGHT_ARM, RagdollPart.LEFT_ARM, RagdollPart.LEFT_ARM, RagdollPart.LEFT_ARM, RagdollPart.RIGHT_LEG, RagdollPart.RIGHT_LEG, RagdollPart.RIGHT_LEG, RagdollPart.LEFT_LEG, RagdollPart.LEFT_LEG, RagdollPart.LEFT_LEG, RagdollPart.RIGHT_ARM, RagdollPart.RIGHT_ARM, RagdollPart.LEFT_ARM, RagdollPart.LEFT_ARM, RagdollPart.RIGHT_LEG, RagdollPart.RIGHT_LEG, RagdollPart.LEFT_LEG, RagdollPart.LEFT_LEG};
        Random rand = new Random();
        return limb_list[rand.nextInt(limb_list.length)];
    }

    public static Limb pickRandomByWeight() {
        Limb[] limb_list = new Limb[]{Limb.HEAD, Limb.CHEST, Limb.CHEST, Limb.RIGHT_ARM, Limb.RIGHT_ARM, Limb.RIGHT_ARM, Limb.LEFT_ARM, Limb.LEFT_ARM, Limb.LEFT_ARM, Limb.RIGHT_LEG, Limb.RIGHT_LEG, Limb.RIGHT_LEG, Limb.LEFT_LEG, Limb.LEFT_LEG, Limb.LEFT_LEG, Limb.RIGHT_HAND, Limb.RIGHT_HAND, Limb.LEFT_HAND, Limb.LEFT_HAND, Limb.RIGHT_FOOT, Limb.RIGHT_FOOT, Limb.LEFT_FOOT, Limb.LEFT_FOOT};
        Random rand = new Random();
        return limb_list[rand.nextInt(limb_list.length)];
    }

    public static Limb pickRandomBottomByWeight() {
        Limb[] limb_list = new Limb[]{Limb.RIGHT_LEG, Limb.LEFT_LEG, Limb.RIGHT_FOOT, Limb.RIGHT_FOOT, Limb.RIGHT_FOOT, Limb.LEFT_FOOT, Limb.LEFT_FOOT, Limb.LEFT_FOOT};
        Random rand = new Random();
        return limb_list[rand.nextInt(limb_list.length)];
    }

    public static void Bite(Player player, Limb target, Float damage, Boolean canBreakBones, Boolean canAmputate) {
        player.getCapability(PlayerHealthProvider.PLAYER_HEALTH_DATA).ifPresent(h->{

            h.applyBleedDamage(target, 5*damage, player);
            h.applyPain(target, 30*damage);
            h.applySkinDamage(target, 5*damage);
            h.applyMuscleDamage(target, 2*damage, player);
            h.setAdrenaline(h.getAdrenaline() + 10*damage);

            if (canBreakBones && chance(0.4F)) {
                h.setLimbFracture(target, getRandomNumber(60, 100));
            }
            if (canAmputate && chance(0.1F)) {
                h.dismember(target);

            }
        });
    }

    public static void Bite(Player player, Limb target, Float damage) {
        Bite(player, target, damage, false, false);
    }

    public static float getRandomNumber(float min, float max) {
        return (float) ((Math.random() * (max - min)) + min);
    }

    public static boolean chance(float num) {
        return Math.random() < num;
    }
}
