import * as mc from "@minecraft/server";
import { GlobalNamespace, Vector3, mclog, spawnEntitySafe, waitForLoopEvent, waitForTicks } from "../../utils/index";
import { Task } from "../../npc/index";
import { PlayDeadComponent } from "../components/index";
const INVALID_RECOVERY_BLOCKS = [
    'minecraft:lava',
    'minecraft:flowing_lava',
    //'minecraft:water',
    //'minecraft:flowing_water',
    'minecraft:powder_snow',
];
export class PlayDeadTask extends Task {
    async performTask() {
        var _a;
        this.options.deathEvents.forEach(event => this.npc.Entity.triggerEvent(event));
        this.npc.Entity.getEffects().forEach(effect => {
            if (effect.typeId !== "hero_of_the_village") {
                this.npc.Entity.removeEffect(effect.typeId);
            }
        });
        this.npc.broadcastEvent("death");
        await this.resetTargetting();
        const wakeTime = mc.system.currentTick + (this.options.deathSeconds * 20);
        const playDeadComponent = this.npc.getComponent(PlayDeadComponent);
        await waitForLoopEvent(5, () => {
            if (mc.system.currentTick >= wakeTime) {
                return true;
            }
            if (this.npc.Health.currentValue > playDeadComponent.PlayDeadThreshold) {
                this.npc.broadcastEvent("early_recovery");
                return true;
            }
            return false;
        }, this.id);
        this.shouldAbort();
        mclog("Restoring Health", "npc");
        (_a = this.npc.Health) === null || _a === void 0 ? void 0 : _a.resetToDefaultValue();
        this.invalidRecoveryTeleport();
        await waitForTicks(this.options.recoverySeconds * 20, this.id);
        this.options.recoveryEvents.forEach(event => this.npc.Entity.triggerEvent(event));
        this.npc.broadcastEvent("recovered");
        return { outcome: "succeeded" };
    }
    async cleanupTask() {
        var _a;
        (_a = this.npc.Health) === null || _a === void 0 ? void 0 : _a.resetToDefaultValue();
        this.options.recoveryEvents.forEach(event => this.npc.Entity.triggerEvent(event));
        this.npc.broadcastEvent("recovered");
    }
    async resetTargetting() {
        var _a;
        if (!this.npc.Entity.getProperty('is_riding')) {
            const cachedLocation = this.npc.Entity.location;
            const entityName = this.npc.Entity.typeId.replace(GlobalNamespace.Namespace, '');
            const deadEntity = spawnEntitySafe(this.npc.Entity.dimension, `${GlobalNamespace.Namespace}dead_${entityName}`, cachedLocation, { rotation: this.npc.Entity.getRotation() });
            deadEntity.setProperty("costume", Number(this.npc.Entity.getProperty("costume")));
            if (this.npc.Hat)
                deadEntity.runCommand(`replaceitem entity @s slot.armor.head 0 ${this.npc.Hat}`);
            (_a = deadEntity.Health) === null || _a === void 0 ? void 0 : _a.setCurrentValue(10);
            this.npc.Entity.addEffect('invisibility', 100, { showParticles: false });
            const callback = (event) => {
                var _a;
                if (event.newValue > event.oldValue) {
                    (_a = this.npc.Entity.Health) === null || _a === void 0 ? void 0 : _a.resetToDefaultValue();
                    mc.world.afterEvents.entityHealthChanged.unsubscribe(callback);
                }
            };
            mc.world.afterEvents.entityHealthChanged.subscribe(callback, { entities: [deadEntity.UnsafeEntity] });
            this.npc.Entity.teleport(new Vector3({ x: cachedLocation.x, y: Math.max(this.npc.Entity.dimension.heightRange.min, cachedLocation.y - 100), z: cachedLocation.z }), { rotation: this.npc.Entity.getRotation() });
            await waitForTicks(80);
            mc.world.afterEvents.entityHealthChanged.unsubscribe(callback);
            this.npc.Entity.teleport(deadEntity.isValid() ? deadEntity.location : cachedLocation, { rotation: this.npc.Entity.getRotation() });
        }
    }
    async invalidRecoveryTeleport() {
        if (this.npc.Entity.location.y <= this.npc.Entity.dimension.heightRange.min) {
            mclog("Recovery Spreadplayer Void");
            this.npc.Entity.runCommand(`spreadplayers ~ ~ 0 20 @s`);
            return;
        }
        while (INVALID_RECOVERY_BLOCKS.some(invalid => { var _a; return (_a = this.getBlock()) === null || _a === void 0 ? void 0 : _a.permutation.matches(invalid); })) {
            mclog("Recovery Spreadplayer Block");
            this.npc.Entity.runCommand(`spreadplayers ~ ~ 0 20 @s`);
            await waitForTicks(1);
        }
    }
    getBlock() {
        return this.npc.Entity.dimension.getBlock(this.npc.Entity.location);
    }
}
