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

import {
    namespace,
    BloomAttack1Damage,
    BloomAttack2FirstDamage,
    BloomAttack2SecondDamage,
    BloomAttack2ThirdDamage,
    BloomAttack3Damage,
    BloomAttack3KnockbackStrength,
    BloomAttack3Range,
    BloomAttack4Damage,
    BloomAttack4KnockbackStrength,
    BloomAttack4Range,
    BloomAttack5Damage,
    BloomAttack7Damage,
    BloomAttack8Damage,
    BloomAttack8KnockbackStrength,
    BloomAttack8Range
} from './config';

system.afterEvents.scriptEventReceive.subscribe((event) => {
    const { sourceEntity: entity, id: eventId } = event;

    const attacks = {
        [`${namespace}:BloomAttack_1`]: attack1,
        [`${namespace}:BloomAttack_2`]: attack2,
        [`${namespace}:BloomAttack_3`]: attack3,
        [`${namespace}:BloomAttack_4`]: attack4,
        [`${namespace}:BloomAttack_5`]: attack5,
        [`${namespace}:BloomAttack_7`]: attack7,
        [`${namespace}:BloomAttack_8`]: attack8
    };

    if (attacks[eventId]) {
        attacks[eventId](entity);
    }
});

function attack1(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_1");

    system.runTimeout(() => {
        if (!entity.isValid()) return;

        const viewDirection = entity.getViewDirection();
        const forwardPos = getForwardPosition(entity, viewDirection, 5);

        const targets = getTargets(entity, forwardPos, 5);
        targets.forEach(target => {
            target.applyDamage(BloomAttack1Damage, { cause: 'entityAttack', damagingEntity: entity });
        });

        entity.runCommandAsync(`execute as @s at @s positioned ^^^5 run camerashake add @a[r=10] 0.1 2.5 positional`);
    }, 74);
}

function attack2(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_2");

    const attackSequence = [
        { damage: BloomAttack2FirstDamage, shake: { intensity: 0.1, duration: 1 }, delay: 36 },
        { damage: BloomAttack2SecondDamage, shake: { intensity: 0.1, duration: 1 }, delay: 34 },
        { damage: BloomAttack2ThirdDamage, shake: { intensity: 0.1, duration: 2 }, delay: 34 }
    ];

    let currentDelay = 74;
    attackSequence.forEach(({ damage, shake, delay }) => {
        system.runTimeout(() => {
            if (!entity.isValid()) return;

            const viewDirection = entity.getViewDirection();
            const forwardPos = getForwardPosition(entity, viewDirection, 5);
            const targets = getTargets(entity, forwardPos, 5);

            targets.forEach(target => {
                target.applyDamage(damage, { cause: 'entityAttack', damagingEntity: entity });
            });

            entity.runCommandAsync(`execute positioned ^^^5 run camerashake add @a[r=10] ${shake.intensity} ${shake.duration} positional`);
        }, currentDelay);
        currentDelay += delay;
    });
}

function attack3(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_3");

    const center = entity.location;
    const yaw = entity.getRotation().y;
    let currentAngle = 90;

    system.runTimeout(() => {
        if (!entity.isValid()) return;

        const loop = system.runInterval(() => {
            if (!entity.isValid()) {
                system.clearRun(loop);
                return;
            }

            const pos = getCircularPosition(center, yaw, currentAngle, BloomAttack3Range);
            const targets = getTargets(entity, pos, 4);

            targets.forEach(target => {
                target.applyDamage(BloomAttack3Damage, { cause: 'entityAttack', damagingEntity: entity });
                applyRadialKnockback(target, center, BloomAttack3KnockbackStrength);
            });

            currentAngle -= 60;
            if (currentAngle <= -270) system.clearRun(loop);
        });
    }, 36);
}

function attack4(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_4");

    const center = entity.location;
    const yaw = entity.getRotation().y;

    function performSweep(startAngle, angleChange, endCondition, onComplete) {
        let angle = startAngle;
        const loop = system.runInterval(() => {
            if (!entity.isValid()) {
                system.clearRun(loop);
                return;
            }

            const pos = getCircularPosition(center, yaw, angle, BloomAttack4Range);
            const targets = getTargets(entity, pos, 6);

            targets.forEach(target => {
                target.applyDamage(BloomAttack4Damage, { cause: 'entityAttack', damagingEntity: entity });
                applyRadialKnockback(target, center, BloomAttack4KnockbackStrength);
            });

            angle += angleChange;
            if (endCondition(angle)) {
                system.clearRun(loop);
                if (onComplete && entity.isValid()) onComplete();
            }
        });
    }

    system.runTimeout(() => {
        if (!entity.isValid()) return;

        performSweep(170, -17, angle => angle <= -5, () => {
            system.runTimeout(() => {
                if (!entity.isValid()) return;

                performSweep(0, 26, angle => angle >= 160, () => {
                    system.runTimeout(() => {
                        if (!entity.isValid()) return;

                        const viewDirection = entity.getViewDirection();
                        const forwardPos = getForwardPosition(entity, viewDirection, 5);
                        const targets = getTargets(entity, forwardPos, 5);

                        targets.forEach(target => {
                            target.applyDamage(BloomAttack4Damage, { cause: 'entityAttack', damagingEntity: entity });
                        });
                    }, 28);
                });
            }, 31);
        });
    }, 33);
}

function attack5(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_5");

    system.runTimeout(() => {
        if (!entity.isValid()) return;

        const viewDirection = entity.getViewDirection();
        const forwardPos = getForwardPosition(entity, viewDirection, 5);
        const targets = getTargets(entity, forwardPos, 7);

        targets.forEach(target => {
            target.applyDamage(BloomAttack5Damage, { cause: 'entityAttack', damagingEntity: entity });
        });
    }, 17);
}

function attack7(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_7");

    system.runTimeout(() => {
        if (!entity.isValid()) return;

        const viewDirection = entity.getViewDirection();
        const entityPos = entity.location;
        const damage = entity.getProperty("bunnystudios_rpg:phase") === 2 ? BloomAttack7Damage * 2 : BloomAttack7Damage;

        const forwardPos = getForwardPosition(entity, viewDirection, 7);
        const targets = getTargets(entity, forwardPos, 5);
        targets.forEach(target => {
            target.applyDamage(damage, { cause: 'entityAttack', damagingEntity: entity });
        });

        const start1 = {
            x: entityPos.x + (viewDirection.x * 14) + (viewDirection.z * 3),
            y: entityPos.y - 4,
            z: entityPos.z + (viewDirection.z * 14) - (viewDirection.x * 3)
        };

        const start2 = {
            x: entityPos.x + (viewDirection.x * 14) - (viewDirection.z * 3),
            y: entityPos.y - 4,
            z: entityPos.z + (viewDirection.z * 14) + (viewDirection.x * 3)
        };

        const end1 = {
            x: entityPos.x + (viewDirection.x * 28) + (viewDirection.z * 3),
            y: entityPos.y + 20,
            z: entityPos.z + (viewDirection.z * 28) - (viewDirection.x * 3)
        };

        const end2 = {
            x: entityPos.x + (viewDirection.x * 28) - (viewDirection.z * 3),
            y: entityPos.y + 20,
            z: entityPos.z + (viewDirection.z * 28) + (viewDirection.x * 3)
        };

        const damageLoop = system.runInterval(() => {
            if (!entity.isValid()) {
                system.clearRun(damageLoop);
                return;
            }
            rotateBloomToNearestPlayer(entity);
            bloomDamageBetweenPoints(entity, start1, end1, damage, 4);
            bloomDamageBetweenPoints(entity, start2, end2, damage, 4);
        }, 10);

        system.runTimeout(() => {
            system.clearRun(damageLoop);
        }, 30);
    }, 31);
}

function attack8(entity) {
    entity.triggerEvent("bunnystudios_rpg:attack_8");
    const center = entity.location;
    const viewDirection = entity.getViewDirection();
    const yaw = entity.getRotation().y;
    const targetPoint = getForwardPosition(entity, viewDirection, 35);
    const damage = entity.getProperty("bunnystudios_rpg:phase") === 2 ? BloomAttack8Damage * 2 : BloomAttack8Damage;

    function performSweep(startAngle, angleChange, endCondition, onComplete) {
        let angle = startAngle;
        const loop = system.runInterval(() => {
            if (!entity.isValid()) {
                system.clearRun(loop);
                return;
            }

            const pos = getCircularPosition(center, yaw, angle, BloomAttack8Range);

            entity.runCommandAsync(
                `execute positioned ${pos.x} ${pos.y} ${pos.z} run damage @e[type=!${namespace}:bloom, family=!inanimate, r=5] ${damage} entity_attack entity @s`
            ).then(() => {
                const targets = getTargets(entity, pos, 4);
                targets.forEach(target => {
                    applyRadialKnockback(target, center, BloomAttack8KnockbackStrength);
                });
            });

            angle += angleChange;
            if (endCondition(angle)) {
                system.clearRun(loop);
                if (onComplete && entity.isValid()) onComplete();
            }
        });
    }

    system.runTimeout(() => {
        if (!entity.isValid()) return;

        performSweep(-5, 12, angle => angle >= 170, () => {
            system.runTimeout(() => {
                if (!entity.isValid()) return;

                performSweep(160, -26, angle => angle <= 0, () => {
                    system.runTimeout(() => {
                        if (!entity.isValid()) return;

                        rotateBloomToNearestPlayer(entity);
                        const start = { x: center.x, y: center.y + 3, z: center.z };
                        const newTargetPoint = getForwardPosition(entity, entity.getViewDirection(), 35);
                        bloomDamageBetweenPoints(entity, start, newTargetPoint, damage, 4);
                    }, 49);
                });
            }, 32);
        });
    }, 15);
}

function getForwardPosition(entity, viewDirection, distance) {
    return {
        x: entity.location.x + (viewDirection.x * distance),
        y: entity.location.y + (viewDirection.y * distance),
        z: entity.location.z + (viewDirection.z * distance)
    };
}

function getCircularPosition(center, yaw, angle, radius) {
    const radians = (yaw + angle) * (Math.PI / 180);
    return {
        x: center.x + radius * Math.cos(radians),
        y: center.y + 1,
        z: center.z + radius * Math.sin(radians)
    };
}

function getTargets(entity, location, maxDistance) {
    return entity.dimension.getEntities({
        location,
        maxDistance,
        excludeTypes: [entity.typeId, "item"],
        excludeFamilies: ["inanimate", "bloom_minion"]
    });
}

function applyRadialKnockback(target, center, strength) {
    const dirX = target.location.x - center.x;
    const dirZ = target.location.z - center.z;
    const magnitude = Math.sqrt(dirX * dirX + dirZ * dirZ);

    target.applyKnockback(
        dirX / magnitude,
        dirZ / magnitude,
        strength * 2.5,
        strength * 0.5
    );
}

function bloomDamageBetweenPoints(entity, start, end, damage, step = 0.5) {
    if (!entity.isValid()) return;

    const dx = end.x - start.x;
    const dy = end.y - start.y;
    const dz = end.z - start.z;

    const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
    const steps = Math.ceil(distance / step);

    for (let i = 0; i <= steps; i++) {
        const t = i / steps;

        const x = start.x + dx * t;
        const y = start.y + dy * t;
        const z = start.z + dz * t;

        entity.runCommandAsync(`execute positioned ${x} ${y} ${z} run damage @e[type=!${namespace}:bloom, family=!inanimate, r=${step + 1}] ${damage} entity_attack entity @s`)
    }
}

function rotateBloomToNearestPlayer(entity) {
    if (!entity.isValid()) return;

    const players = world.getAllPlayers().filter(player => player.getGameMode() === "survival" || player.getGameMode() === "adventure");
    if (!players.length) return;

    let nearestPlayer = players[0];
    let minDistance = Number.MAX_VALUE;

    for (const player of players) {
        const dx = player.location.x - entity.location.x;
        const dy = player.location.y - entity.location.y;
        const dz = player.location.z - entity.location.z;
        const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

        if (distance < minDistance) {
            minDistance = distance;
            nearestPlayer = player;
        }
    }

    const deltaX = nearestPlayer.location.x - entity.location.x;
    const deltaZ = nearestPlayer.location.z - entity.location.z;
    const yRot = -(Math.atan2(deltaX, deltaZ)) / Math.PI * 180;

    entity.setRotation({
        x: entity.getRotation().x,
        y: yRot
    });
}