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

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

import { Utils } from "./Utils";

export class BossLevel extends EntityObjectComponent {
    setAttack(id, duration) {
        this.getObject().setAttack(id, duration);
    }

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

    get isAttacking() {
        return this.getObject().isAttacking();
    }

    debugAttack(name) {
        //log(`${this?.constructor?.name}/${this.getObject()?.constructor?.name}`, "Attack: " + name);
    }

    disableAttack() {
        this.data.attacking = false;
    }

    onAttackTick(type, tick) {}
    onRun() {}
    onStartLevel() {}

    onMeleeAttack() {}
    onRangedAttack() {}
}

export class BossEntity extends EntityObject {
    __old_location;
    __old_rotation;
    __sfx_time = 9999;
    __attackingTime = 0;

    onBegin() {
        this.data["attacking"] = false;
        this.data["transforming"] = -1;
        this.data["kill"] = -1;
        this.data["level"] = 0;
        this.data["health"] = 1000;
        this.data["last_attack"] = 0;
        this.data["init"] = false;
        this.data["spawnPoint"] = this.getEntity().location;
    }

    onHealthChanged(newValue, oldValue) {
        this.data["health"] = newValue;
    }

    getHealth() {
        return this.data["health"];
    }

    onKill() {
        this.data["kill"] = 0;

        this.clearComponents();
    }

    isKilled() {
        return this.data["kill"] >= 0;
    }

    getLevel() {
        return this.data["level"];
    }

    isTransforming() {
        return this.data["transforming"] > 0;
    }

    setAttack(type, duration) {
        this.data["attacking"] = Math.round(duration * TicksPerSecond);

        this.data["attack_type"] = type;

        this.__attackingTime = 0;

        this.setProperty("pixell_bbo:attack", type);
    }

    isAttacking() {
        return this.data["attacking"] > 0;
    }

    onCreate() {
        this.data["attacking"] = -1;

        this.setProperty("pixell_bbo:attack", 0);

        this.__old_location = this.getEntity().location;

        if (!this.isTransforming()) {
            this.addComponent(this.getComponentFor(this.data["level"]));
        }
    }

    isFixRotation() {
        return false;
    }

    getAwakeDistance() {
        return 10;
    }

    onTick() {
        this.setProperty("pixell_bbo:init", Boolean(this.data["init"]));
        if (!this.data["init"]) {
            const entity = this.getEntity();
            const block = entity.dimension.getBlockFromRay(this.data["spawnPoint"], {
                x: 0,
                y: -1,
                z: 0,
            });
            let targetLocation = this.data["spawnPoint"];
            try {
                targetLocation = block?.block?.above()?.location ?? targetLocation;
            } catch (e) {}
            if (
                entity.location.x != targetLocation.x &&
                entity.location.z != targetLocation.z &&
                entity.location.y != targetLocation.y
            ) {
                entity.teleport(targetLocation, { rotation: { x: 0, y: 0 } });
            }
            entity.addEffect("resistance", TicksPerSecond, {
                amplifier: 255,
                showParticles: false,
            });
            entity.addEffect("instant_health", TicksPerSecond, {
                amplifier: 255,
                showParticles: false,
            });
            if (
                entity.dimension
                    .getPlayers({
                        maxDistance: this.getAwakeDistance(),
                        location: entity.location,
                    })
                    .filter((a) => {
                        return (
                            a.getGameMode() == GameMode.survival ||
                            a.getGameMode() == GameMode.adventure
                        );
                    }).length > 0
            ) {
                this.data["init"] = true;
                this.triggerEntityEvent("pixell_bbo:init");
                Utils.post(() => {
                    const healthComponent = entity.getComponent(EntityHealthComponent.componentId);
                    healthComponent.resetToMaxValue();
                }, 0.1);
            }
            return;
        }
        //world.stopMusic();
        if (this.__sfx_time > TicksPerSecond * this.getBackgrondSound().time) {
            /* this.getEntity().dimension.playSound(this.getBackgrondSound().id, this.getEntity().location, {
                volume: 1.0
            }); */
            this.__sfx_time = 0;
        }

        this.__sfx_time++;

        /* try { this.setProperty("pixell_bbo:attacking", this.isAttacking()); } catch(e) {}
        try { this.setProperty("pixell_bbo:transforming", this.isTransforming()); } catch(e) {}
        try { this.setProperty("pixell_bbo:killed", this.isKilled()); } catch(e) {} */

        try {
            if (this.isAttacking() || this.isTransforming() || this.isKilled()) {
                this.getEntity().clearVelocity();

                if (this.isFixRotation()) {
                    this.getEntity().teleport(this.__old_location, {
                        facingLocation: this.__old_rotation,
                    });
                } else {
                    this.getEntity().teleport(this.__old_location);
                }
            } else {
                this.__old_location = this.getEntity().location;

                this.__old_rotation = {
                    x: this.__old_location.x + this.getEntity().getViewDirection().x * 10,
                    y: this.__old_location.y + this.getEntity().getViewDirection().y * 10,
                    z: this.__old_location.z + this.getEntity().getViewDirection().z * 10,
                };
            }
        } catch (e) {}

        if (this.isAttacking()) {
            this.setProperty("pixell_bbo:attacking", true);
            this.setProperty("pixell_bbo:transforming", false);
            this.setProperty("pixell_bbo:killed", false);

            this.__attackingTime++;

            this.data["last_attack"] = 0;

            this.data["attacking"]--;

            this.onAttackTick(this.data["attack_type"], this.data["attacking"]);
            this.setProperty(
                "pixell_bbo:state_time",
                parseFloat(this.data["attacking"]) / TicksPerSecond
            );

            if (this.__attackingTime > 3) {
                this.setProperty("pixell_bbo:attack", 0);
            } else {
                this.setProperty("pixell_bbo:attack", this.data["attack_type"] ?? 0);
            }
        } else if (this.isTransforming()) {
            this.setProperty("pixell_bbo:attacking", false);
            this.setProperty("pixell_bbo:transforming", true);
            this.setProperty("pixell_bbo:killed", false);

            this.data["last_attack"] = 0;

            this.clearComponents();

            if (this.data["transforming"] == this.getTransfromTime()) {
                this.onBeginTransform(this.data["level"] + 1);
            }

            this.data["transforming"]--;

            this.onTransforming(this.data["transforming"]);
            this.setProperty(
                "pixell_bbo:state_time",
                parseFloat(this.data["transforming"]) / TicksPerSecond
            );

            if (this.data["transforming"] == 0) {
                system.runTimeout(() => {
                    try {
                        this.data["level"]++;

                        this.onEndTransform(this.data["level"]);

                        this.addComponent(this.getComponentFor(this.data["level"]));

                        this.#callComponentEvent("onStartLevel");
                    } catch (e) {
                        //   log("Error", e);
                    }
                }, 1);
            }
        } else if (this.isKilled()) {
            this.data["last_attack"] = 0;

            this.setProperty("pixell_bbo:attacking", false);
            this.setProperty("pixell_bbo:transforming", false);
            this.setProperty("pixell_bbo:killed", true);

            this.data["kill"]++;

            this.onAfterKill(this.data["kill"]);
            this.setProperty(
                "pixell_bbo:state_time",
                parseFloat(this.data["kill"]) / TicksPerSecond
            );
        } else {
            this.setProperty("pixell_bbo:attacking", false);
            this.setProperty("pixell_bbo:transforming", false);
            this.setProperty("pixell_bbo:killed", false);

            this.data["last_attack"]++;
            this.onRun();
            this.setProperty(
                "pixell_bbo:state_time",
                parseFloat(this.data["last_attack"]) / TicksPerSecond
            );
        }
    }

    onBeginTransform(toLevel) {}

    getBackgrondSound() {
        return {
            id: "",
            time: 9999,
        };
    }

    getComponentFor(level) {}

    /** BP EVENT  */
    onTransform() {
        if (!this.isTransforming()) {
            this.data["transforming"] = this.getTransfromTime();
        }
    }

    onThrowProjectile(entity) {
        entity.remove();

        if (!this.data["init"]) return;
        if (this.isAttacking()) return;

        if (Utils.featchViewEntities(this, 8, 3, 3).length > 0) {
            this.onMeleeAttack();

            return;
        }

        this.onRangedAttack();
    }

    onEndTransform(level) {}

    onAfterKill(tick) {}

    onAttackTick(type, tick) {
        this.#callComponentEvent("onAttackTick", type, tick);
    }

    #callComponentEvent(name, ...args) {
        try {
            this.getComponents().forEach((comp) => {
                if (comp[name]) {
                    comp[name](...args);
                }
            });
        } catch (e) {
            // log("ComponentFunction", name, e);
        }
    }

    getMeleeRange() {
        return 7;
    }

    onRun() {
        this.#callComponentEvent("onRun");

        try {
            if (this.data["last_attack"] > TicksPerSecond * 2) {
                let attackMelee = false;

                Utils.featchViewEntities(this, this.getMeleeRange(), 4, 4).forEach((e) => {
                    if (e?.entity?.typeId == "minecraft:player") {
                        attackMelee = true;
                    }
                });

                if (attackMelee) {
                    this.onMeleeAttack();
                }
            }
        } catch (e) {
            // this.log("AttackMelee[Error]", e);
        }
    }

    onMeleeAttack() {
        if (!this.data["init"]) return;
        this.#callComponentEvent("onMeleeAttack");
    }

    onRangedAttack() {
        if (!this.data["init"]) return;
        this.#callComponentEvent("onRangedAttack");
    }

    onTransforming(ticks) {
        this.getEntity().addEffect("slowness", TicksPerSecond, {
            amplifier: 255,
            showParticles: false,
        });

        this.getEntity().addEffect("resistance", TicksPerSecond, {
            amplifier: 255,
            showParticles: false,
        });

        this.getEntity().addEffect("instant_health", TicksPerSecond, {
            amplifier: 8,
            showParticles: false,
        });
    }

    log(...data) {
        //  log(this.constructor.name, ...data);
    }

    getTransfromTime() {
        return 0;
    }
}
