import { TicksPerSecond, system, world } from "@minecraft/server";
import { EntityRuntime } from "../global_api/EntityRuntime";
import { Vector2, Vector3 } from "../global_api/Math";
import { BossEntity, BossLevel } from "./BossLevel";
import { Utils } from "./Utils";

function post(callback, secs) {
    system.runTimeout(() => {
        try {
            callback();
        } catch (e) {
            // log("Error", e);
        }
    }, secs * TicksPerSecond);
}

class FirstLevel extends BossLevel {
    playerNextTime = 0;
    vomitDamage = 0;
    roarDamage = 0;

    /** @returns {Craterus} */
    getObject() {
        return super.getObject();
    }

    onRun() {
        super.onRun();

        const entity = this.getEntity();
    }

    onHealthChanged(newValue, oldValue) {
        this.vomitDamage += Math.max(oldValue - newValue, 0);

        this.roarDamage += Math.max(oldValue - newValue, 0);
    }

    onMeleeAttack() {
        if (
            this.getEntity().dimension.getEntities({
                location: this.getEntity().location,
                maxDistance: 3,
                type: "minecraft:player",
            }).length > 0 &&
            Math.random() < 0.4
        ) {
            this.playerNextTime = 0;

            this.onForceJump();
        } else {
            let random = Math.round(Math.random() * 4);
            if (random <= 1) {
                this.onForceTap();
            } else if (random <= 2) {
                this.onForceKick();
            } else {
                this.onForceRoar();
            }
        }
    }

    onRam() {
        if (this.roarDamage > 30) {
            this.onForceRoar();

            this.roarDamage = 0;
        }
    }

    onForceJump() {
        this.debugAttack("JUMP");

        this.setAttack(1, 2);

        const entity = this.getEntity();

        entity.triggerEvent("pixell_bbo:idle");

        entity.clearVelocity();

        entity.applyImpulse({ x: 0, y: 1.5, z: 0 });

        const location = entity.location;

        post(() => {
            entity.dimension
                .getEntities({
                    location: location,
                    maxDistance: 10,
                })
                .forEach((e) => {
                    if (e.typeId.startsWith("minecraft:")) {
                        e.applyDamage(14);

                        Utils.applyForce(e, {
                            x: (e.location.x - entity.location.x) * 2,
                            y: 1,
                            z: (e.location.z - entity.location.z) * 2,
                        });
                    }
                });

            entity.triggerEvent("pixell_bbo:move");
        }, 1.9);
    }

    onForceTap() {
        this.setAttack(Math.random() > 0.5 ? 4 : 5, 1);

        const entity = this.getEntity();

        entity.triggerEvent("pixell_bbo:idle");

        post(() => {
            const attackDamage = 10;

            entity.dimension
                .getEntities({
                    location: entity.location,
                    maxDistance: 10,
                })
                .forEach((e) => {
                    if (e.typeId.startsWith("minecraft:")) {
                        Utils.applyForce(e, {
                            x: (e.location.x - entity.location.x) * 2,
                            y: 0,
                            z: (e.location.z - entity.location.z) * 2,
                        });

                        e.applyDamage(attackDamage);
                    }
                });

            post(() => {
                entity.triggerEvent("pixell_bbo:move");
            }, 0.5);
        }, 1);
    }

    onForceRoar() {
        const attackDamage = 9;

        this.setAttack(7, 1.5);

        const entity = this.getEntity();
        const view = entity.getViewDirection();

        this.obtainEntityInRange(15).forEach((rayEntity) => {
            rayEntity.entity.applyDamage(attackDamage);

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

            rayEntity.entity.addEffect("poison", TicksPerSecond * 5, { showParticles: false });
        });
    }

    onForceKick() {
        const attackDamage = 7;

        this.setAttack(Math.random() > 0.5 ? 2 : 3, 1);

        const entity = this.getEntity();

        entity.triggerEvent("pixell_bbo:idle");

        entity.clearVelocity();

        post(() => {
            entity.dimension
                .getEntities({
                    location: entity.location,
                    maxDistance: 7,
                })
                .forEach((e) => {
                    if (e.typeId.startsWith("minecraft:")) {
                        e.applyDamage(attackDamage);

                        Utils.applyForce(e, {
                            x: (e.location.x - entity.location.x) * 0.8,
                            y: 1.0,
                            z: (e.location.z - entity.location.z) * 0.8,
                        });
                    }
                });

            entity.triggerEvent("pixell_bbo:move");
        }, 1);
    }

    onForceVomit() {
        this.setAttack(6, 3);

        const entity = this.getEntity();

        entity.triggerEvent("pixell_bbo:idle");

        const viewDir = entity.getViewDirection();

        const location = entity.location;

        post(() => {
            for (let x = -0.5; x <= 0.5; x += 1.0) {
                for (let z = -0.5; z <= 0.5; z += 1.0) {
                    const block = entity.dimension.getBlockFromRay(
                        {
                            x: entity.location.x + x + entity.getViewDirection().x * 2,
                            z: entity.location.z + z + entity.getViewDirection().z * 2,
                            y: entity.location.y,
                        },
                        { x: 0, y: -1, z: 0 },
                        { maxDistance: 4, excludeTypes: ["pixell_bbo:craterus_vomiting"] }
                    );

                    block.block.above().setType("pixell_bbo:craterus_vomiting");
                }
            }

            entity.triggerEvent("pixell_bbo:move");
        }, 3);
    }

    obtainEntityInRange(distance) {
        return Utils.featchViewEntities(this.getObject(), distance, 2, 2);
    }
}

class LastLevel extends BossLevel {
    roarDamage = 0;
    sonic = 0;
    vomit = 0;

    onStartLevel() {
        this.data.fix_location = this.getEntity().location;

        const block = this.getEntity().dimension.getBlockFromRay(this.getEntity().location, {
            x: 0,
            y: -1,
            z: 0,
        });

        if (block) {
            this.data.fix_location = block.block.above().location;
        }
    }

    onRam() {
        if (!this.getObject().isAttacking()) {
            super.onMeleeAttack();
        }
    }

    obtainEntityInRange(distance) {
        return Utils.featchViewEntities(this.getObject(), distance, 2, 2);
    }

    onMeleeAttack() {
        const players = this.getEntity().dimension.getEntities({
            location: this.getEntity().location,
            maxDistance: 128,
            type: "minecraft:player",
            closest: true,
        });

        let badOfChange = 0.3;

        let playerDistance =
            players.length > 0
                ? new Vector3(players[0].location).distance(this.getEntity().location)
                : 9999;

        if (players.length > 0 && playerDistance > 16) {
            badOfChange = 0.8;
        }

        if (playerDistance < 3 && Math.random() > 0.3) {
            this.onForceFangs();
        } else if (Math.random() < badOfChange) {
            this.roarDamage = 0;

            this.onForceBadWolf();
        } else if (Math.random() < (playerDistance > 12 ? 0.6 : 0.2)) {
            this.sonic = 0;

            this.onForceSonicRoar();
        } else if (Math.random() < (playerDistance < 6 ? 0.9 : 0.3)) {
            this.onForceFangs();
        } else {
            this.onForceNotCongate();
        }
    }

    onHealthChanged(newValue, oldValue) {
        this.roarDamage += Math.max(oldValue - newValue, 0);

        this.sonic += Math.max(oldValue - newValue, 0);

        this.vomit += Math.max(oldValue - newValue, 0);
    }

    onRun() {
        const players = this.getEntity().dimension.getEntities({
            location: this.getEntity().location,
            maxDistance: 128,
            type: "minecraft:player",
        });

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

    onForceVomit() {
        this.debugAttack("ACID");

        this.setAttack(4, 3);

        this.data["attacking"] = true;

        const entity = this.getEntity();

        const viewDir = entity.getViewDirection();

        const location = entity.location;

        post(() => {
            for (let x = -0.5; x <= 0.5; x += 1.0) {
                for (let z = -0.5; z <= 0.5; z += 1.0) {
                    const block = entity.dimension.getBlockFromRay(
                        {
                            x: entity.location.x + x + entity.getViewDirection().x * 2,
                            z: entity.location.z + z + entity.getViewDirection().z * 2,
                            y: entity.location.y,
                        },
                        { x: 0, y: -1, z: 0 },
                        { maxDistance: 4, excludeTypes: ["pixell_bbo:craterus_vomiting"] }
                    );

                    block.block.above().setType("pixell_bbo:craterus_vomiting");
                }
            }
        }, 3);
    }

    onForceNotCongate() {
        this.debugAttack("PLEASE CLEAR TEETH");

        const entity = this.getEntity();

        this.setAttack(5, 1);

        post(() => {
            entity.dimension
                .getEntities({
                    location: entity.location,
                    maxDistance: 20,
                })
                .forEach((e) => {
                    if (e.typeId.startsWith("minecraft:")) {
                        const view = entity.getViewDirection();

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

                        e.applyDamage(2);

                        e.addEffect("minecraft:poison", TicksPerSecond * 10);
                    }
                });
        }, 1);
    }

    onForceSonicRoar() {
        this.debugAttack("SONIC ROAR");

        this.setAttack(1, 1);

        const entity = this.getEntity();

        this.obtainEntityInRange(20).forEach((rayEntity) => {
            const e = rayEntity.entity;

            if (e.typeId.startsWith("minecraft:")) {
                Utils.applyForce(e, { x: 0, y: 1, z: 0 });

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

                e.addEffect("minecraft:poison", TicksPerSecond * 10);
            }

            e.applyDamage(7);
        });
    }

    onForceFangs() {
        this.debugAttack("FANGS");

        this.setAttack(3, 4);

        const entity = this.getEntity();

        post(() => {
            Utils.featchViewEntities(this.getObject(), 7, 6, 6).forEach((rayEntity) => {
                const e = rayEntity.entity;

                if (e.typeId.startsWith("minecraft:")) {
                    post(() => {
                        Utils.applyForce(e, {
                            x: entity.getViewDirection().x * 4,
                            y: 1,
                            z: entity.getViewDirection().z * 4,
                        });
                    }, 0.5);

                    e.applyDamage(5);
                }
            });
        }, 1.8);
    }

    onForceBadWolf() {
        const attackDamage = 9;

        this.debugAttack("BAD WOLF");

        this.setAttack(2, 5);

        const entity = this.getEntity();

        let entities = [];

        this.obtainEntityInRange(20).forEach((rayEntity) => entities.push(rayEntity.entity));

        for (let i = 0; i < 50; i++) {
            post(
                () =>
                    entities.forEach((e) => {
                        e.addEffect("slow_falling", 5, { showParticles: false, amplifier: 200 });

                        Utils.applyForce(e, {
                            x: (entity.location.x - e.location.x) / 4,
                            y: (entity.location.y - e.location.y) / 4,
                            z: (entity.location.z - e.location.z) / 4,
                        });
                    }),
                (1 / TicksPerSecond) * i
            );
        }

        post(
            () =>
                entities.forEach((e) => {
                    //e.addEffect("slow_falling", 5, { showParticles: false, amplifier: 200 })
                    const view = entity.getViewDirection();

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

                    e.applyDamage(attackDamage);
                }),
            2.5
        );
    }

    /** @returns {Craterus} */
    getObject() {
        return super.getObject();
    }
}

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

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

    getBackgrondSound() {
        return {
            id: "pixell_bbo.bg.craterus",
            time: 34,
        };
    }

    onTick() {
        super.onTick();
    }

    onEndTransform(level) {
        super.onEndTransform(level);

        this.triggerEntityEvent("pixell_bbo:level_two");

        const entity = this.getEntity();
        const fix = Math.random() * 78;
        for (let rotY of [0, 120, 240]) {
            const position = new Vector2(0, 0).toAngle(8 + Math.random() * 6, rotY + fix);
            entity.dimension.spawnEntity("pixell_bbo:aphemus", {
                x: entity.location.x + position.x,
                z: entity.location.z + position.y,
                y: entity.location.y + 3,
            });
        }
    }

    onAfterKill(tick) {
        super.onAfterKill(tick);

        if (tick > TicksPerSecond * 6.5) {
            this.getEntity().remove();
        }
    }

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

        this.triggerEntityEvent("pixell_bbo:idle");

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

    getTransfromTime() {
        return TicksPerSecond * 5;
    }
}

world.beforeEvents.worldInitialize.subscribe((event) => {
    event.blockComponentRegistry.registerCustomComponent("pixell_bbo:craterus_vomit", {
        onTick: ({ block }) => {
            if (system.currentTick % (20 * 0.5) === 0) {
                system
                    .waitTicks(10)
                    .then(() => {
                        block.dimension
                            .getEntities({
                                location: block.center(),
                                maxDistance: 1,
                                excludeTypes: ["pixell_bbo:craterus"],
                            })
                            .forEach((e) => {
                                if (e?.typeId?.startsWith("minecraft:")) {
                                    e?.applyDamage(1);

                                    e?.addEffect("poison", TicksPerSecond * 2, {
                                        showParticles: false,
                                    });
                                }
                            });
                    })
                    .catch((e) => {});
            }
            /*  if (system.currentTick % (20 * 5) === 0) {
                const chance = 20;
                const random = Math.round(Math.random() * 100);

                if (random <= chance) {
                    block.setType("minecraft:air");
                }
            } */
        },
    });
});

EntityRuntime.register("pixell_bbo:craterus", Craterus);
