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

import { EntityObject, EntityRuntime } from "../global_api/EntityRuntime";

import { Utils } from "./Utils";

import { Vector2, Vector3 } from "../global_api/Math";

import { AttackRandomizer } from "../utils/AttackRandomizer";
import { getRandomBetween } from "../utils/utils";
import { BossEntity, BossLevel } from "./BossLevel";

class SweetLevel extends BossLevel {}

class FirstLevel extends SweetLevel {
    static ATTACK_PATH_OF_LIGHT = 1;
    static ATTACK_BLESSING = 2;
    static ATTACK_ALL_SEEING = 3;
    static ATTACK_JUDGMENT = 4;
    static ATTACK_WIND = 5;
    static ATTACK_JUDGMENT2 = 6;
    static ATTACK_BERSERK = 7;

    constructor() {
        super();

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

            if (entity.typeId == "pixell_bbo:sweetgabriel") {
                switch (eventId) {
                    case "pixell_bbo:shield": {
                        const nearbyPlayers = entity.dimension.getPlayers({
                            location: entity.location,
                            maxDistance: 8,
                        });

                        for (let player of nearbyPlayers) {
                            player.applyKnockback(
                                player.location.x < entity.location.x ? -1 : 1,
                                player.location.z < entity.location.z ? -1 : 1,
                                10,
                                0.4
                            );
                        }

                        system.waitTicks(0.1 * 20).then(() => {
                            this.doBerserk();
                        });

                        break;
                    }
                    case "pixell_bbo:hit_shield": {
                        entity.dimension.playSound("random.anvil_land", entity.location);
                        break;
                    }

                    default: {
                        break;
                    }
                }
            }
        });
    }

    onRun() {
        super.onRun();

        const entity = this.getEntity();

        const players = entity.dimension.getEntities({
            location: entity.location,
            maxDistance: 256,
            type: "minecraft:player",
        });

        if (players.length > 0) {
            entity.teleport(this.getEntity().location, {
                facingLocation: players[0].location,
            });
        }
    }

    onRangedAttack() {
        super.onRangedAttack();

        if (Math.random() < 0.8) {
            this.onMeleeAttack();
        }
    }

    onTick() {
        super.onTick();

        const entity = this.getEntity();

        entity.addEffect("slowness", 20, { showParticles: false, amplifier: 255 });
    }

    rotationBlessing = 0;
    onAttackTick(type, tick) {
        super.onAttackTick(type, tick);
        if (type == FirstLevel.ATTACK_BLESSING) {
            const entity = this.getEntity();
            entity.teleport(entity.location, { rotation: { x: 0, y: this.rotationBlessing } });
            this.rotationBlessing += 0.2;
        }
    }

    doJudgment() {
        this.debugAttack("Judgment");
        this.setAttack(FirstLevel.ATTACK_JUDGMENT, 8);

        const entity = this.getEntity();
        const roation =
            new Vector2(entity.getViewDirection().x, entity.getViewDirection().z).getDegress() - 90;
        const offset1 = new Vector2(20, 0).rotate(roation);
        const offset2 = new Vector2(-20, 0).rotate(roation);

        entity.dimension.spawnEntity("pixell_bbo:sweet_eye", {
            x: entity.location.x + offset1.x,
            z: entity.location.z + offset1.y,
            y: entity.location.y + 3,
        });
        entity.dimension.spawnEntity("pixell_bbo:sweet_eye", {
            x: entity.location.x + offset2.x,
            z: entity.location.z + offset2.y,
            y: entity.location.y + 3,
        });
    }

    doJudment2() {
        this.debugAttack("Judgment 2");
        const view = this.getEntity().getViewDirection();
        this.setAttack(FirstLevel.ATTACK_JUDGMENT2, 4);
        Utils.post(() => {
            for (let i = 0; i < 5; i++) {
                Utils.post(() => {
                    Utils.featchViewEntities(this.getObject(), 18, 10, 5).forEach((e) => {
                        if (e.entity.typeId.startsWith("minecraft:")) {
                            e.entity.applyDamage(5);
                            Utils.applyForce(e.entity, {
                                x: view.x * 3,
                                y: 0,
                                z: view.z * 3,
                            });
                            e.entity.dimension.spawnParticle(
                                "pixell_bbo:sweet_meteor",
                                e.entity.location
                            );
                        }
                    });
                }, i * 0.2);
            }
        }, 2.12);
    }

    doWind() {
        const attackDamage = 8;

        this.debugAttack("Wind");

        this.setAttack(FirstLevel.ATTACK_WIND, 1.2);

        const entity = this.getEntity();

        entity.dimension.playSound("pixell_bbo.sweet1.wind", this.getEntity().location);

        entity.dimension
            .getEntities({
                location: entity.location,
                maxDistance: 32,
                type: "minecraft:player",
            })
            .forEach((e) => {
                const forceXZ = new Vector2(10, 0).toAngle(
                    8,
                    new Vector2(
                        e.location.x - entity.location.x,
                        e.location.z - entity.location.z
                    ).getDegress()
                );

                Utils.applyForce(e, {
                    x: forceXZ.x,
                    y: 0,
                    z: forceXZ.y,
                });

                e.applyDamage(attackDamage);
            });
    }

    doSeeing() {
        const attackDamage = 7;

        this.debugAttack("Seeing");

        this.setAttack(FirstLevel.ATTACK_ALL_SEEING, 9.1);

        const self = this.getEntity();

        const dimension = self.dimension;

        for (let t = 0; t < 6; t += 0.2) {
            Utils.post(() => {
                const view = self.getViewDirection();

                Utils.featchViewEntities(this.getObject(), 5, 6, 3).forEach(({ entity }) => {
                    if (entity.typeId.startsWith("minecraft")) {
                        entity.applyDamage(attackDamage);

                        Utils.applyForce(entity, {
                            x: view.x * 4,
                            y: view.y * 1,
                            z: view.z * 4,
                        });
                        //entity.setOnFire(4);
                    }
                });
            }, t + 2);
        }
    }

    doBlessing() {
        const attackDamage = 4;

        this.debugAttack("Blessing");

        const self = this.getEntity();

        this.setAttack(FirstLevel.ATTACK_BLESSING, 6);

        let attackDuration = 5 * 20;

        const runId = system.runInterval(() => {
            attackDuration--;

            if (attackDuration <= 0) {
                system.clearRun(runId);
                return;
            }

            const location = self.location;

            const targets = self.dimension.getPlayers({
                location: location,
                maxDistance: 24,
            });

            for (let player of targets) {
                const targetLocation = player.location;

                system.waitTicks(0.3 * 20).then(() => {
                    const isLogged = world.getAllPlayers().includes(player);

                    if (!isLogged) return;

                    const playerAtAttackPos =
                        player.dimension.getEntitiesAtBlockLocation(targetLocation)[0];

                    if (!playerAtAttackPos) return;

                    playerAtAttackPos.applyDamage(attackDamage);
                });
            }
        }, 0);
    }

    doPathOfLight() {
        const dimension = this.getEntity().dimension;

        this.debugAttack("Path of Night");

        this.setAttack(FirstLevel.ATTACK_PATH_OF_LIGHT, 3);
        const view = this.getEntity().getViewDirection();

        for (let x = 0; x < 5; x++) {
            const post = { location: undefined };

            Utils.post(() => {
                const attackDamage = 6;

                const targets = Utils.featchViewEntities(this.getObject(), 20, 7, 14);

                dimension.playSound("pixell_bbo.sweet1.path_of_light", this.getEntity().location);
                if (targets.length > 0) {
                    targets.forEach((e) => {
                        let x = e.entity;
                        //post.location = targets[Math.min(targets.length-1, Math.round(targets.length*Math.random()))].entity.location;
                        dimension.spawnParticle("pixell_bbo:sweet_meteor_impact", x.location);
                        dimension.spawnParticle("pixell_bbo:sweet_meteor", x.location);

                        x.applyDamage(attackDamage);
                        Utils.applyForce(x, {
                            x: view.x * 2,
                            y: 0.5,
                            z: view.z * 2,
                        });
                    });
                }
            }, 0.1 + Math.max(0, (x - 1) * 0.4));
            /* Utils.post(()=>{
                if(!post.location) return;


                dimension.getEntities({type: "minecraft:player", location: post.location, maxDistance: 1.5}).forEach(e => {
                })
            }, 0.4 * x); */
        }
    }

    doBerserk() {
        const attackDamage = 8;

        this.debugAttack("Berserk");

        const self = this.getEntity();

        this.setAttack(FirstLevel.ATTACK_BERSERK, 8);

        for (let t = 2; t < 7; t += 0.5) {
            Utils.post(() => {
                let attacks = [];

                Utils.featchViewEntities(this.getObject(), 10, 3, 20).forEach(({ entity }) => {
                    if (entity.typeId.startsWith("minecraft:")) {
                        const location = {
                            x: entity.location.x,
                            y: entity.location.y + 10,
                            z: entity.location.z,
                        };

                        entity.dimension.spawnParticle("pixell_bbo:sweet_laser_d", location);

                        location.y -= 10;

                        attacks.push(location);
                    }
                });

                Utils.post(() => {
                    for (let location of attacks) {
                        self.dimension.spawnParticle("pixell_bbo:sweet_ray", location);

                        self.dimension
                            .getEntities({
                                location: location,
                                maxDistance: 5,
                            })
                            .forEach((e) => {
                                if (e.typeId.startsWith("minecraft:")) {
                                    e.applyDamage(attackDamage);
                                }
                            });
                    }
                }, 1);
            }, t);
        }
    }

    attackRandomzier = new AttackRandomizer(6);

    onMeleeAttack() {
        const closePlayers = Utils.featchViewEntities(this.getObject(), 6, 6, 3).filter(
            (rayEntity) => rayEntity.entity.typeId == "minecraft:player"
        );

        const farPlayers = Utils.featchViewEntities(this.getObject(), 16, 8, 3).filter(
            (rayEntity) => rayEntity.entity.typeId == "minecraft:player"
        );

        const random_close = getRandomBetween(0, 5);
        const random_far = getRandomBetween(0, 4);

        if (closePlayers.length > 0 && Math.random() < 0.75) {
            // 75% of change of melee attack
            switch (random_close) {
                case 0: {
                    this.doSeeing();
                    break;
                }
                case 1: {
                    this.doJudgment();
                    break;
                }
                case 2: {
                    this.doPathOfLight();
                    break;
                }
                case 3: {
                    this.doJudment2();
                    break;
                }
                case 4: {
                    this.doWind();
                    break;
                }
                case 5: {
                    this.doBlessing();
                    break;
                }

                default: {
                    break;
                }
            }

            return;
        }

        if (farPlayers.length > 0 && Math.random() < 0.75) {
            // 75% of change of melee attack
            switch (random_far) {
                case 0: {
                    this.doSeeing();
                    break;
                }
                case 1: {
                    this.doJudgment();
                    break;
                }
                case 2: {
                    this.doPathOfLight();
                    break;
                }
                case 3: {
                    this.doJudment2();
                    break;
                }
                case 4: {
                    this.doBlessing();
                    break;
                }

                default: {
                    break;
                }
            }

            return;
        }
    }
}

class LastLevel extends SweetLevel {
    static ATTACK_BASIC = 2;
    static ATTACK_PATH_LIGHT = FirstLevel.ATTACK_PATH_OF_LIGHT;
    static ATTACK_REDEMPTION = 3;
    static ATTACK_BERSERK = 4;
    static ATTACK_WINGS = 5;
    static ATTACK_SWORD = 6;

    doWings() {
        const attackDamage = 9;

        this.debugAttack("Wings");

        const self = this.getEntity();

        this.setAttack(LastLevel.ATTACK_WINGS, 2.5);

        const view = self.getViewDirection();

        Utils.post(() => {
            Utils.featchViewEntities(this.getObject(), 30, 6, 20).forEach(({ entity }) => {
                if (entity.typeId != "minecraft:player") return;

                Utils.applyForce(entity, {
                    x: view.x * 4,
                    y: 1,
                    z: view.z * 4,
                });

                entity.applyDamage(attackDamage);
            });
        }, 1);
    }

    doBerserk() {
        const attackDamage = 8;

        this.debugAttack("Berserk");

        const self = this.getEntity();

        this.setAttack(LastLevel.ATTACK_BERSERK, 8);

        for (let t = 2; t < 7; t += 0.5) {
            Utils.post(() => {
                let attacks = [];

                Utils.featchViewEntities(this.getObject(), 10, 3, 20).forEach(({ entity }) => {
                    if (entity.typeId.startsWith("minecraft:")) {
                        const location = {
                            x: entity.location.x,
                            y: entity.location.y + 10,
                            z: entity.location.z,
                        };

                        entity.dimension.spawnParticle("pixell_bbo:sweet_laser_d", location);

                        location.y -= 10;

                        attacks.push(location);
                    }
                });

                Utils.post(() => {
                    for (let location of attacks) {
                        self.dimension.spawnParticle("pixell_bbo:sweet_ray", location);

                        self.dimension
                            .getEntities({
                                location: location,
                                maxDistance: 5,
                            })
                            .forEach((e) => {
                                if (e.typeId.startsWith("minecraft:")) {
                                    e.applyDamage(attackDamage);
                                }
                            });
                    }
                }, 1);
            }, t);
        }
    }

    doSimpleSword() {
        const attackDamage = 7;

        this.debugAttack("Simple Sword");
        this.setAttack(LastLevel.ATTACK_BASIC, 1.5);
        Utils.post(() => {
            const view = this.getEntity().getViewDirection();
            Utils.featchViewEntities(this.getObject(), 10, 3, 10).forEach(({ entity }) => {
                if (entity?.typeId?.startsWith("minecraft:")) {
                    entity.applyDamage(attackDamage);
                    Utils.applyForce(entity, {
                        x: view.x * 3,
                        y: 0.1,
                        z: view.z * 3,
                    });
                }
            });
        }, 0.1);
    }

    doRedemption() {
        const attackDamage = 10;

        this.debugAttack("Redemption");

        const self = this.getEntity();

        this.setAttack(LastLevel.ATTACK_REDEMPTION, 2);

        const view = self.getViewDirection();

        Utils.featchViewEntities(this.getObject(), 10, 3, 10).forEach(({ entity }) => {
            if (entity.typeId.startsWith("minecraft:")) {
                entity.applyDamage(attackDamage);

                Utils.applyForce(entity, {
                    x: view.x * 3,
                    z: view.z * 1,
                    y: view.y * 2,
                });
            }
        });
    }

    onRangedAttack() {
        super.onRangedAttack();

        if (Math.random() < 0.75) {
            this.onMeleeAttack();
        }
    }

    doPathOfLight() {
        const attackDamage = 10;

        const dimension = this.getEntity().dimension;

        this.debugAttack("Path of Night");

        this.setAttack(FirstLevel.ATTACK_PATH_OF_LIGHT, 3);

        const players = Utils.featchViewEntities(this.getObject(), 25, 6, 3).filter(
            (rayEntity) => rayEntity.entity.typeId == "minecraft:player"
        );

        if (players.length > 0) {
            const player = players[Math.round(Math.random() * (players.length - 1))];

            dimension.spawnParticle("pixell_bbo:sweet_meteor_impact", player.entity.location);

            Utils.post(() => {
                dimension.playSound("pixell_bbo.sweet1.path_of_light", this.getEntity().location);
                dimension
                    .getEntities({
                        location: player.entity.location,
                        maxDistance: 7,
                    })
                    .forEach((e) => {
                        if (e.typeId.startsWith("minecraft:")) {
                            const view = this.getEntity().getViewDirection();
                            Utils.applyForce(e, {
                                x: view.x * 4,
                                y: 1,
                                z: view.z * 4,
                            });

                            dimension.spawnParticle("pixell_bbo:sweet_meteor_impact", e.location);
                            e.applyDamage(attackDamage);
                            e.runCommand("camerashake  add @s 5 2");
                        }
                    });
            }, 1.7);
        }
    }

    onMeleeAttack() {
        /*   const players = Utils.featchViewEntities(this.getObject(), 16, 6, 3).filter(
            (rayEntity) => rayEntity.entity.typeId == "minecraft:player"
        );

        if (players.length > 0 && Math.random() < 0.8) {
            let attack = Math.round(Math.random() * 4);
            if (attack <= 1) {
                this.doWings();
            } else if (attack <= 2) {
                this.doRedemption();
            } else {
                this.doSimpleSword();
            }
        } else {
            let attack = Math.round(Math.random() * 4);
            if (attack <= 1) this.doPathOfLight();
            else if (attack <= 2) this.doBerserk();
            else if (attack <= 3) this.doWings();
        }
 */
        const closePlayers = Utils.featchViewEntities(this.getObject(), 6, 6, 3).filter(
            (rayEntity) => rayEntity.entity.typeId == "minecraft:player"
        );

        const farPlayers = Utils.featchViewEntities(this.getObject(), 16, 8, 3).filter(
            (rayEntity) => rayEntity.entity.typeId == "minecraft:player"
        );

        const random_close = getRandomBetween(0, 2);
        const random_far = getRandomBetween(0, 2);

        if (closePlayers.length > 0 && Math.random() < 0.75) {
            // 75% of change of melee attack
            switch (random_close) {
                case 0: {
                    this.doWings();
                    break;
                }
                case 1: {
                    this.doRedemption();
                    break;
                }
                case 2: {
                    this.doSimpleSword();
                    break;
                }

                default: {
                    break;
                }
            }

            return;
        }

        if (farPlayers.length > 0 && Math.random() < 0.75) {
            // 75% of change of melee attack
            switch (random_far) {
                case 0: {
                    this.doWings();
                    break;
                }
                case 1: {
                    this.doBerserk();
                    break;
                }
                case 2: {
                    this.doPathOfLight();
                    break;
                }

                default: {
                    break;
                }
            }

            return;
        }
    }
}

class SweetGabriel extends BossEntity {
    firstMode = new FirstLevel();
    lastMode = new LastLevel();

    getComponentFor(level) {
        if (level == 0) {
            return this.firstMode;
        } else {
            return this.lastMode;
        }
    }

    onBegin() {
        this.data.health = 200;

        super.onBegin();
    }

    getTransfromTime() {
        return TicksPerSecond * 5.5;
    }

    getMeleeRange() {
        return 16;
    }

    getAwakeDistance() {
        return 10;
    }

    onTransforming(ticks) {
        super.onTransforming(ticks);

        this.setProperty("pixell_bbo:level_two", true);
    }

    onEndTransform() {
        super.onEndTransform();

        this.triggerEntityEvent("pixell_bbo:level_two");

        this.triggerEntityEvent("pixell_bbo:move");

        Utils.post(() => {
            this.getEntity().runCommand("event entity @s pixell_bbo:move"); // I Love Minecraft bugs!!
        }, 1);
    }

    onAfterKill(ticks) {
        super.onAfterKill(ticks);
        if (ticks > TicksPerSecond * 3) {
            this.getEntity().remove();
        }
    }
}

class SweetEye extends EntityObject {
    onBegin() {
        this.data.life = TicksPerSecond * 60;
    }

    onDamage() {
        this.getEntity().remove();
    }

    onTick() {
        this.data.life--;

        const entity = this.getEntity();

        entity.addEffect("slow_falling", TicksPerSecond * 5, {
            showParticles: false,
            amplifier: 255,
        });

        entity.addEffect("resistance", TicksPerSecond * 5, {
            showParticles: false,
            amplifier: 255,
        });

        let players = entity.dimension.getEntities({
            location: entity.location,
            maxDistance: 128,
            type: "minecraft:player",
        });

        let rotation = 0;

        if (players.length > 0) {
            rotation = players[0].getRotation().y;
        }

        let playerLocation = this.data["playerLocation"];

        if (players.length > 0) {
            playerLocation = players[0].location;

            this.data["playerLocation"] = playerLocation;
        }

        if (!playerLocation) return;

        if (playerLocation) {
            const vec = new Vector3(playerLocation).sub(entity.location);

            const speed = {
                x: (Math.abs(vec.x) > 1 ? Math.sign(vec.x) : vec.x) * 0.2,
                y: (Math.abs(vec.y) > 1 ? Math.sign(vec.y) : vec.y) * 0.2,
                z: (Math.abs(vec.z) > 1 ? Math.sign(vec.z) : vec.z) * 0.2,
            };

            entity.applyImpulse(speed);

            entity.setRotation({ x: 0, y: rotation - 180 });
        }

        if (this.data.life <= 0) {
            if (this.data.life == 0) {
                entity.playAnimation("animation.pixell_bbo.sweet_eye.explosion");
            } else if (this.data.life < -2) {
                if (!playerLocation) {
                    entity.remove();
                } else {
                    const selfLocation = entity.location;

                    entity.dimension.spawnParticle("pixell_bbo:sweet_explosion", selfLocation);

                    players = entity.dimension.getEntities({
                        location: entity.location,
                        maxDistance: 4,
                        type: "minecraft:player",
                    });

                    entity.remove();

                    if (players.length > 0) {
                        players.forEach((p) => {
                            p.applyDamage(6);

                            p.addEffect("levitation", TicksPerSecond * 3);
                        });
                    }
                }
            }
        } else if (playerLocation && this.data.life > 1) {
            if (new Vector3(playerLocation).distance(entity.location) < 2) {
                this.data.life = 1;
            }
        }
    }
}

EntityRuntime.register("pixell_bbo:sweetgabriel", SweetGabriel);

EntityRuntime.register("pixell_bbo:sweet_eye", SweetEye);
