import * as mc from "@minecraft/server";
import * as utils from "../utils/index";
import { NPCBase } from "./base";
import { CompanionBackupComponent, DebugComponent, DestructEventComponent, DestructOnRemoveEventComponent, PlayerInteract, OnHurtEventComponent, OnOwnerHurtEventComponent } from "./components/index";
/**
 * @remarks A child of the {@link NPCBase} class designed for pets or other entities tamed to a player owner.
 */
export class Companion extends NPCBase {
    /**
     * @remarks The {@link utils.SafetyCheckedPlayer} that owns this NPC.
     * Note that this is not actually related to the owner with the `minecraft:is_tamed` component and will default to the first player in the world if it has not been assigned.
     * Assigning an owner is done by saving the id of this NPC to the owning player's `companion_id` dynamic property, tagging this NPC with `tame_target`, then having the owning
     * player run the scriptevent `<project_namespace>:tamed_check`.
     */
    get Owner() {
        var _a;
        return (_a = this.owner) !== null && _a !== void 0 ? _a : utils.getPlayersSafe()[0];
    }
    /**
     * Adds {@link DestructEventComponent}, {@link DestructOnRemoveEventComponent}, and {@link DebugComponent} to the NPC on creation.
     * Then adds {@link PlayerInteract}, {@link OnHurtEventComponent}, and {@link OnOwnerHurtEventComponent}
     * before calling the {@link onOwnerFound} method once the owner has been found.
     */
    constructor(entity, defaultTask) {
        super(entity, defaultTask);
        this.addComponent(new DestructEventComponent());
        this.addComponent(new DestructOnRemoveEventComponent());
        this.addComponent(new DebugComponent());
        this.addComponent(new PlayerInteract(this.onPlayerInteract.bind(this)));
        this.findOwner().then(owner => {
            utils.mclog(`Set ${this.ID}'s Owner To ${owner.name}`, "npc");
            this.owner = owner;
            this.addComponent(new OnHurtEventComponent());
            this.addComponent(new OnOwnerHurtEventComponent());
            this.onOwnerFound();
        });
    }
    /**
     * Attempts to restore a backup of the entity when removed.
     * If this is undesired, have the entity run `scriptevent <project_namespace>:on_destruct` which will destroy the entity without trying to restore a backup.
     */
    async destructor(reason) {
        var _a;
        try {
            (_a = this.getComponent(CompanionBackupComponent)) === null || _a === void 0 ? void 0 : _a.onDespawn();
        }
        catch (error) { }
        super.destructor(reason);
    }
    /**
     * @remarks Checks if a player has a companion.
     * @param player The player who's companions to look for.
     * @param companionQuery The query for the companion search.
     * @returns True if the player does have a companion (meaning an instance of the Companion class has been associated with a valid entity), false otherwise.
     */
    static hasCompanion(player, companionQuery) {
        const npcs = NPCBase.getNPCS(player.dimension, companionQuery);
        return npcs.some(npc => {
            if (npc.TypeId !== Companion.name && npc.Owner.id === player.id) {
                return true;
            }
        });
    }
    async findOwner() {
        var _a;
        const ownerName = (_a = this.Entity.getComponent("tameable")) === null || _a === void 0 ? void 0 : _a.tamedToPlayerId;
        return new Promise(async (resolve) => {
            if (ownerName) {
                await utils.waitForLoopEvent(5, () => {
                    var _a;
                    const player = (_a = utils.getEntitySafe(ownerName)) === null || _a === void 0 ? void 0 : _a.getPlayer();
                    if (player)
                        resolve(player);
                    return player !== undefined;
                });
            }
            else {
                utils.mclog(`${this.ID} waiting for tame`, "tame");
                const tameEvent = (event) => {
                    var _a;
                    const player = (_a = this.Entity.getComponent("tameable")) === null || _a === void 0 ? void 0 : _a.tamedToPlayer;
                    if (player)
                        resolve(new utils.SafetyCheckedPlayer(player));
                    mc.world.afterEvents.dataDrivenEntityTrigger.unsubscribe(tameEvent);
                };
                mc.world.afterEvents.dataDrivenEntityTrigger.subscribe(tameEvent, { entities: [this.Entity.UnsafeEntity], eventTypes: ["on_tame"] });
            }
        });
    }
    onOwnerAssigned(newOwner) { }
    onOwnerFound() { }
    onPlayerInteract(player) { }
}
