import { EntityDamageCause, system, world } from "@minecraft/server";

import { executeRandomizedMove } from "../utils/utils";

const bosses = ["pixell_bbo:unknown"];

function trackHealthStep(entity, stepSize, healthMap, callback) {
    const id = entity.id;

    const currentHealth = entity.getComponent("health").currentValue;

    if (!healthMap[id]) healthMap[id] = currentHealth;

    const lastHealth = healthMap[id];

    const lastStep = Math.floor(lastHealth / stepSize);

    const currentStep = Math.floor(currentHealth / stepSize);

    const stepsLost = lastStep - currentStep;

    if (stepsLost > 0) {
        callback(entity, stepsLost);
    }

    healthMap[id] = currentHealth;
}

const healthMap_1 = {};

world.afterEvents.entityHurt.subscribe((event) => {
    const { damage, damageSource, hurtEntity } = event;

    const damagingEntity = damageSource?.damagingEntity;

    if (damageSource?.damagingEntity?.typeId == "minecraft:player") {
        const consecutivesAttacks = damageSource?.damagingEntity?.getDynamicProperty(
            "pixell_bbo:consecutivesAttacks"
        );

        if (consecutivesAttacks == undefined) {
            damageSource?.damagingEntity?.setDynamicProperty("pixell_bbo:consecutivesAttacks", 0);
        }

        if (bosses.includes(hurtEntity.typeId)) {
            trackHealthStep(hurtEntity, 100, healthMap_1, (entity) => {
                entity.triggerEvent("self:summon_mode");
            });

            if (hurtEntity.getProperty("self:attack_type") == "melee") {
                damageSource?.damagingEntity?.setDynamicProperty(
                    "pixell_bbo:consecutivesAttacks",
                    consecutivesAttacks + 1
                );

                if (consecutivesAttacks == 5) {
                    if (hurtEntity.getProperty("self:stage") == 2) {
                        system.runTimeout(() => {
                            hurtEntity.addEffect("invisibility", 0.45 * 20, {
                                amplifier: 1,
                                showParticles: false,
                            });

                            hurtEntity.applyImpulse({
                                x: hurtEntity.getViewDirection().x,
                                y: 0.2,
                                z: hurtEntity.getViewDirection().z,
                            });

                            damageSource?.damagingEntity?.applyKnockback(
                                hurtEntity.getViewDirection().x,
                                hurtEntity.getViewDirection().z,
                                3,
                                0.5
                            );
                        }, 0.7 * 20);

                        hurtEntity.triggerEvent("self:shadow_slash");
                    }
                }

                if (consecutivesAttacks >= 10) {
                    system.runTimeout(() => {
                        hurtEntity.addEffect("invisibility", 0.45 * 20, {
                            amplifier: 1,
                            showParticles: false,
                        });

                        hurtEntity.tryTeleport(
                            {
                                x:
                                    damageSource?.damagingEntity?.location.x -
                                    damageSource?.damagingEntity?.getViewDirection().x * 8,
                                y: damageSource?.damagingEntity?.location.y,
                                z:
                                    damageSource?.damagingEntity?.location.z -
                                    damageSource?.damagingEntity?.getViewDirection().z * 8,
                            },
                            {
                                checkForBlocks: true,
                            }
                        );
                    }, 0.7 * 20);

                    hurtEntity.triggerEvent("self:teleport");

                    damageSource?.damagingEntity?.setDynamicProperty(
                        "pixell_bbo:consecutivesAttacks",
                        0
                    );
                }
            }
        }
    }

    if (damageSource && damageSource?.damagingEntity?.typeId == "pixell_bbo:unknown") {
        /**
         * @type {number | undefined}
         */
        const stage = damagingEntity.getProperty("self:stage");

        if (stage == 1) {
            hurtEntity.dimension.spawnParticle("pixell_bbo:shadow_clawn", {
                x: hurtEntity.location.x,
                y: hurtEntity.location.y + 1.5,
                z: hurtEntity.location.z,
            });

            const random = Math.round(Math.random() * 100);

            const chance = 20;

            if (random <= chance) {
                hurtEntity.addEffect("darkness", 2 * 20, {
                    amplifier: 0,
                    showParticles: false,
                });
            }
        }

        if (stage == 2) {
            hurtEntity.dimension.spawnParticle("pixell_bbo:shadow_clawn2", {
                x: hurtEntity.location.x,
                y: hurtEntity.location.y + 1.5,
                z: hurtEntity.location.z,
            });

            const random = Math.round(Math.random() * 100);

            const chance = 20;

            if (random <= chance) {
                hurtEntity.addEffect("darkness", 2 * 20, {
                    amplifier: 0,
                    showParticles: false,
                });

                hurtEntity.addEffect("wither", 2 * 20, {
                    amplifier: 0,
                    showParticles: false,
                });

                hurtEntity.setOnFire(3);
            }
        }

        const targetPropName = "ninja:target";

        const targetProp = damagingEntity.getDynamicProperty(targetPropName);

        if (!targetProp) {
            damagingEntity.setDynamicProperty(targetPropName, hurtEntity.id);

            return;
        }

        damagingEntity.setDynamicProperty(targetPropName, hurtEntity.id);
    }
});

system.runInterval(() => {
    const players = world.getPlayers();

    for (let player of players) {
        const vortexes = player.dimension.getEntities({
            location: player.location,
            closest: 1,
            type: "pixell_bbo:dark_vortex",
            maxDistance: 16,
        });

        const tornados = player.dimension.getEntities({
            location: player.location,
            closest: 1,
            type: "pixell_bbo:tornado",
            maxDistance: 12,
        });

        const unknown = player.dimension.getEntities({
            location: player.location,
            closest: 1,
            type: "pixell_bbo:unknown",
            maxDistance: 4,
        });

        for (let tentacle of unknown) {
            if (
                tentacle.getProperty("self:stage") == 2 &&
                tentacle.getProperty("self:dark_tentacles") == true
            ) {
                const dx = tentacle.location.x - player.location.x;

                const dz = tentacle.location.z - player.location.z;

                const distance = Math.sqrt(dx * dx + dz * dz);

                if (distance < 1) continue;

                const nx = dx / distance;

                const nz = dz / distance;

                const strength = 0.5;

                const vertical = 0.2;

                player.applyKnockback(-nx, -nz, strength, vertical);

                player.applyDamage(1, {
                    damagingEntity: tentacle,
                    cause: EntityDamageCause.magic,
                });
            }
        }

        for (let vortex of vortexes) {
            const dx = vortex.location.x - player.location.x;

            const dz = vortex.location.z - player.location.z;

            const distance = Math.sqrt(dx * dx + dz * dz);

            if (distance < 0.1) continue;

            const nx = dx / distance;

            const nz = dz / distance;

            const strength = 0.5;

            const vertical = 0.1;

            player.applyDamage(1, {
                damagingEntity: vortex,
                cause: EntityDamageCause.magic,
            });

            player.applyKnockback(nx, nz, strength, vertical);

            player.addEffect("darkness", 5 * 20, { amplifier: 0, showParticles: false });
        }

        for (let tornado of tornados) {
            const dx = tornado.location.x - player.location.x;

            const dz = tornado.location.z - player.location.z;

            const distance = Math.sqrt(dx * dx + dz * dz);

            if (distance < 1) continue;

            const nx = dx / distance;

            const nz = dz / distance;

            const strength = 0.5;

            const vertical = 0.1;

            player.applyDamage(1, {
                damagingEntity: tornado,
                cause: EntityDamageCause.magic,
            });

            player.applyKnockback(-nx, -nz, strength, vertical);
        }
    }
}, 1);

world.afterEvents.projectileHitEntity.subscribe((event) => {
    const { source, projectile } = event;

    const entity = event.getEntityHit().entity;

    if (bosses.includes(entity.typeId)) {
        if (projectile.typeId.includes("arrow")) {
            if (entity.getProperty("self:stage") == 2) {
                entity.triggerEvent("self:clone_of_shadows");

                system.runTimeout(() => {
                    entity.dimension.spawnEntity("pixell_bbo:unknown_clone", {
                        x: entity.location.x + entity.getViewDirection().x * 1.5,
                        y: entity.location.y,
                        z: entity.location.z + entity.getViewDirection().z * 1.5,
                    });

                    entity.dimension.spawnEntity("pixell_bbo:unknown_clone", {
                        x: entity.location.x + entity.getViewDirection().x * 3,
                        y: entity.location.y,
                        z: entity.location.z + entity.getViewDirection().z * 3,
                    });
                }, 1.8 * 20);
            }
        }
    }
});

const Phase1Moves = new Map([
    [
        "dark_vortex",
        {
            weight: 3,
            duration: 2.0,
            properties: [
                {
                    name: "self:attacks",
                    value: "dark_vortex",
                },
            ],
            event: "self:dark_vortex",
            time_down_event: "self:dark_vortex_remove",
        },
    ],
    [
        "dark_storm",
        {
            weight: 5,
            duration: 1.5,
            properties: [
                {
                    name: "self:attacks",
                    value: "dark_storm",
                },
            ],
            event: "self:summon_mode",
            time_down_event: "self:summon_mode_remove",
        },
    ],
    [
        "teleport",
        {
            weight: 10,
            duration: 2.0,
            properties: [
                {
                    name: "self:attacks",
                    value: "none",
                },
            ],
            event: "self:teleport",
            time_down_event: "self:teleport_remove",
        },
    ],
]);

const Phase2Moves = new Map([
    [
        "dark_vortex",
        {
            weight: 8,
            duration: 2.0,
            properties: [
                {
                    name: "self:attacks",
                    value: "dark_vortex",
                },
            ],
            event: "self:dark_vortex",
            time_down_event: "self:dark_vortex_remove",
        },
    ],
    [
        "dark_storm",
        {
            weight: 8,
            duration: 1.5,
            properties: [
                {
                    name: "self:attacks",
                    value: "dark_storm",
                },
            ],
            event: "self:summon_mode",
            time_down_event: "self:summon_mode_remove",
        },
    ],
    [
        "teleport",
        {
            weight: 10,
            duration: 2.0,
            properties: [
                {
                    name: "self:attacks",
                    value: "none",
                },
            ],
            event: "self:teleport",
            time_down_event: "self:teleport_remove",
        },
    ],
    [
        "occult_magic",
        {
            weight: 12,
            duration: 2.0,
            properties: [
                {
                    name: "self:attacks",
                    value: "occult_magic",
                },
            ],
            event: "self:occult_magic",
            time_down_event: "self:occult_magic_remove",
        },
    ],
    [
        "dark_tentacles",
        {
            weight: 10,
            duration: 2.0,
            properties: [
                {
                    name: "self:attacks",
                    value: "dark_tentacles",
                },
            ],
            event: "self:dark_tentacles",
            time_down_event: "self:dark_tentacles_remove",
        },
    ],
]);

world.afterEvents.dataDrivenEntityTrigger.subscribe((event) => {
    const { entity, eventId } = event;

    try {
        if (bosses.includes(entity.typeId)) {
            let health = entity?.getComponent("health");

            if (eventId == "self:random_event") {
                executeRandomizedMove(entity, Phase1Moves);
            }

            if (eventId == "self:random_event_phase_2") {
                executeRandomizedMove(entity, Phase2Moves);
            }

            if (eventId == "self:summon_mode") {
                if (entity.getProperty("self:summon") == false) {
                    entity.setProperty("self:summon", true);
                } else {
                    entity.setProperty("self:summon", false);
                }

                entity.dimension.spawnEntity("pixell_bbo:tornado", {
                    x: entity.location.x + entity.getViewDirection().x * 1.5,
                    y: entity.location.y,
                    z: entity.location.z + entity.getViewDirection().z * 1.5,
                });

                system.runTimeout(() => {
                    entity.triggerEvent("self:summon_mode_remove");
                }, 1.2 * 20);
            }

            if (eventId == "self:night_curse") {
                if (entity.getProperty("self:stage") == 2) {
                    if (health.currentValue <= 300 && health.currentValue > 0) {
                        if (entity.getProperty("self:night_curse2") == false) {
                            entity.setProperty("self:night_curse2", true);
                        }
                    }
                } else {
                    if (health.currentValue <= 160 && health.currentValue > 0) {
                        if (entity.getProperty("self:night_curse") == false) {
                            entity.setProperty("self:night_curse", true);
                        }
                    }
                }

                entity.runCommand("time set midnight");

                system.runTimeout(() => {
                    entity.runCommand("time set day");
                }, 60 * 20);
            }

            if (eventId == "self:dying") {
                health.setCurrentValue(1);

                entity.addEffect("instant_health", 10 * 20, { showParticles: false, amplifier: 1 });

                entity.addTag("pixell_bbo:dying");
            }

            if (eventId == "self:die") {
                entity.runCommand("time set day");
            }

            if (eventId == "self:second_form") {
                entity.removeTag("pixell_bbo:dying");
            }

            if (eventId == "self:clone_of_shadows") {
                system.runTimeout(() => {
                    entity?.triggerEvent("self:clone_of_shadows_remove");
                }, 2 * 20);
            }

            if (eventId == "self:shadow_slash") {
                system.runTimeout(() => {
                    entity?.triggerEvent("self:shadow_slash_remove");
                }, 1.5 * 20);
            }

            if (eventId == "self:occult_magic") {
                if (entity.getProperty("self:summon3") == false) {
                    entity.setProperty("self:summon3", true);
                } else {
                    entity.setProperty("self:summon3", false);
                }

                const players = entity.dimension.getPlayers({
                    location: entity.location,
                    maxDistance: 12,
                });

                for (let player of players) {
                    player.addEffect("minecraft:slowness", 5 * 20, {
                        amplifier: 2,
                        showParticles: true,
                    });

                    player.setOnFire(5);

                    system.runTimeout(() => {
                        player.applyDamage(1, {
                            cause: EntityDamageCause.magic,
                            damagingEntity: entity,
                        });
                    });

                    system.runTimeout(() => {
                        player.applyDamage(1, {
                            cause: EntityDamageCause.magic,
                            damagingEntity: entity,
                        });
                    }, 1 * 20);

                    system.runTimeout(() => {
                        player.applyDamage(1, {
                            cause: EntityDamageCause.magic,
                            damagingEntity: entity,
                        });
                    }, 2 * 20);

                    system.runTimeout(() => {
                        player.applyDamage(1, {
                            cause: EntityDamageCause.magic,
                            damagingEntity: entity,
                        });
                    }, 3 * 20);

                    system.runTimeout(() => {
                        player.applyDamage(1, {
                            cause: EntityDamageCause.magic,
                            damagingEntity: entity,
                        });
                    }, 4 * 20);

                    system.runTimeout(() => {
                        player.applyDamage(1, {
                            cause: EntityDamageCause.magic,
                            damagingEntity: entity,
                        });
                    }, 5 * 20);
                }
            }

            if (eventId == "self:default") {
                system.runTimeout(() => {
                    entity?.triggerEvent("self:basic_attack");
                }, 2.8 * 20);
            }

            if (eventId == "self:teleport") {
                const targetPropName = "ninja:target";

                const targetProp = entity?.getDynamicProperty(targetPropName);

                if (!targetProp) {
                    system.runTimeout(() => {
                        entity?.triggerEvent("self:teleport_remove");
                    }, 0.45 * 20);

                    return;
                }

                const targetEntity = entity.dimension.getEntities().find((e) => e.id == targetProp);

                if (!targetEntity) {
                    entity?.triggerEvent("self:teleport_remove");

                    return;
                }

                //  teleportBehindTarget(entity, targetEntity);

                const dashPower = 3;

                entity.applyImpulse({
                    x: entity.location.x < targetEntity.location.x ? dashPower : -dashPower,
                    y: 0.2,
                    z: entity.location.z < targetEntity.location.z ? dashPower : -dashPower,
                });

                entity?.triggerEvent("self:teleport_remove");
            }

            if (eventId == "self:dark_tentacles") {
                entity.setProperty("self:dark_tentacles", true);

                system.runTimeout(() => {
                    entity.setProperty("self:dark_tentacles", false);
                }, 15 * 20);

                system.runTimeout(() => {
                    entity?.triggerEvent("self:dark_tentacles_remove");
                }, 1.5 * 20);
            }

            if (eventId == "self:attack_anim") {
                system.runTimeout(() => {
                    entity?.triggerEvent("self:attack_anim_remove");
                }, 0.25 * 20);
            }
        }
    } catch (error) {}
});
