import { EntityComponentTypes, GameMode, Player, system, world } from "@minecraft/server";
import { EntityObject, EntityRuntime } from "../global_api/EntityRuntime";
import { ArenaMap } from "../ui/portal";
import { getPlayerLastEnteredPortal, resetPlayerLastEnteredPortal } from "../utils/utils";

class PlayerReference extends EntityObject {
    updateInterval = 20;

    onUpdate() {
        const entity = this.getEntity();

        if (entity.hasTag("pixell_bbo:finish_fight")) {
            if (Math.abs(Date.now() - entity.getDynamicProperty("lastFinishFight")) / 1000 > 30) {
                entity.removeTag("pixell_bbo:finish_fight");
            }
        }
    }
}

EntityRuntime.register("minecraft:player", PlayerReference);

world.afterEvents.entityDie.subscribe((event) => {
    const { deadEntity, damageSource } = event;

    if (deadEntity instanceof Player) {
        const inArena = deadEntity.getDynamicProperty("pixell_bbo:in_arena") ?? false;

        if (!inArena) return;

        resetPlayerGamemode(deadEntity);

        resetPlayerLastEnteredPortal(deadEntity);
    }
});

/**
 *
 * @param {Player} player
 */
export function setPlayerArenaGamemode(player) {
    const playerCurrentGamemode = player.getGameMode();

    if (playerCurrentGamemode != GameMode.adventure) {
        const lastGamemode = player.getDynamicProperty("pixell_bbo:lastGamemode");

        player.setDynamicProperty("pixell_bbo:lastGamemode", playerCurrentGamemode);
        player.setDynamicProperty("pixell_bbo:in_arena", true);

        player.setGameMode(GameMode.adventure);

        player.setDynamicProperty("pixell_bbo:in_arena", true);
    }
}

const ArenaFogs = new Map([
    [
        "pixell_bbo:sweetgabriel",
        {
            fog: {
                id: "pixell_bbo:fog_sweetgabriel",
                providedId: "pixell_bbo:fog_sweetgabriel",
            },
        },
    ],
]);

/**
 *
 * @param {Player} player
 */
export function setPlayerArenaFog(player, arenaId) {
    const fogData = ArenaFogs.get(arenaId);

    if (!fogData) return;

    const {
        fog: { id, providedId },
    } = fogData;

    player.runCommand(`fog @s push ${id} ${providedId}`);
}

/**
 *
 * @param {Player} player
 */
export function clearPlayerFog(player) {
    const fogs = ArenaFogs.values();

    for (const fogData of fogs) {
        const {
            fog: { providedId },
        } = fogData;

        player.runCommand(`fog @s pop ${providedId}`);
    }
}

/**
 *
 * @param {Player} player
 */
export function resetPlayerGamemode(player) {
    player.setDynamicProperty("pixell_bbo:in_arena", false);
    player.removeTag("pixell_bbo:teleporting_to_arena");

    const gamemode = player.getDynamicProperty("pixell_bbo:lastGamemode");

    player.setGameMode(GameMode[gamemode]);

    player.setDynamicProperty("pixell_bbo:lastGamemode");

    player.setDynamicProperty("pixell_bbo:in_arena", false);
}

const AFK_TIMEOUT = 300 * 20; // 5 minutes = 300 ticks
const NO_DAMAGE_TIMEOUT = 300 * 20; // 5 minutes = 300 ticks

const PLAYER_LOOP = system.runInterval(() => {
    const players = world.getAllPlayers();
    for (const player of players) {
        playerSpawnProtectionTimer(player);
        playerAFKHandler(player);
        playerNoDamageHandler(player);
        removeEntitiesFromArena(player);
    }
}, 0);

/**
 *
 * @param {Player} player
 * @returns
 */
function playerAFKHandler(player) {
    try {
        const propertyName = "pixell_bbo:last_entered_portal";

        /**
         * @type {Vector3}
         */
        const lastPortalPosition = getPlayerLastEnteredPortal(player);

        const lastPortal = lastPortalPosition;

        const afkTimeout = player.getDynamicProperty("pixell_bbo:afk_timeout") ?? 0;

        // console.warn(`AFK Timeout: ${afkTimeout}`);

        const isMoving = Object.values(player.inputInfo.getMovementVector()).some((v) => v !== 0);

        const playerGamemode = player.getGameMode();

        if (playerGamemode !== GameMode.adventure) {
            player.setDynamicProperty("pixell_bbo:afk_timeout", 0);
            return;
        }

        if (!isMoving) {
            player.setDynamicProperty(
                "pixell_bbo:afk_timeout",
                Math.min(AFK_TIMEOUT, afkTimeout + 1)
            );

            if (afkTimeout >= AFK_TIMEOUT) {
                player.teleport(lastPortal, {
                    dimension: world.getDimension("overworld"),
                });

                resetPlayerGamemode(player);

                // resetPlayerLastEnteredPortal(player);

                player.setDynamicProperty("pixell_bbo:afk_timeout", 0);

                return;
            }
        } else {
            player.setDynamicProperty("pixell_bbo:afk_timeout", 0);
        }
    } catch (error) {}
}

/**
 *
 * @param {Player} player
 * @returns
 */
function playerNoDamageHandler(player) {
    try {
        const afkTimeout = player.getDynamicProperty("pixell_bbo:no_damage_timeout") ?? 0;

        //  console.warn(`No Damage Timeout: ${afkTimeout}`);

        /**
         * @type {Vector3}
         */
        const lastPortalPosition = getPlayerLastEnteredPortal(player);

        const lastPortal = lastPortalPosition;

        const playerGamemode = player.getGameMode();

        if (playerGamemode !== GameMode.adventure) {
            player.setDynamicProperty("pixell_bbo:no_damage_timeout", 0);
            return;
        }

        const health = player.getComponent(EntityComponentTypes.Health);

        if (!health) return;

        const { currentValue } = health;

        const newHealth = player.getComponent(EntityComponentTypes.Health).currentValue;

        if (newHealth == currentValue) {
            player.setDynamicProperty(
                "pixell_bbo:no_damage_timeout",
                Math.min(NO_DAMAGE_TIMEOUT, afkTimeout + 1)
            );

            if (afkTimeout >= NO_DAMAGE_TIMEOUT) {
                player.teleport(lastPortal, {
                    dimension: world.getDimension("overworld"),
                });

                resetPlayerGamemode(player);

                // resetPlayerLastEnteredPortal(player);

                player.setDynamicProperty("pixell_bbo:no_damage_timeout", 0);

                return;
            }
        } else {
            player.setDynamicProperty("pixell_bbo:no_damage_timeout", 0);
        }
    } catch (error) {}
}

/**
 *
 * @param {Player} player
 */
function removeEntitiesFromArena(player) {
    const arenas = ArenaMap.entries();

    for (let arena of arenas) {
        const [key, value] = arena;

        const { offsetX, height } = value;

        const entities = world.getDimension("the_end").getEntities({
            location: {
                x: offsetX,
                y: height,
                z: offsetX,
            },
            volume: {
                x: 64,
                y: 64,
                z: 64,
            },
            families: ["enderman"],
        });

        for (let entity of entities) {
            entity.remove();
        }
    }
}

/**
 *
 * @param {Player} player
 */
function playerSpawnProtectionTimer(player) {
    const currentTime = player.getDynamicProperty("pixel_studios::player::spawn_protection") ?? 0;

    const seconds = currentTime / 20;

    if (currentTime > 0) {
        player.setDynamicProperty(
            "pixel_studios::player::spawn_protection",
            Math.max(0, currentTime - 1)
        );

        player.onScreenDisplay.setActionBar(`§bSPAWN PROTECTION: ${seconds.toFixed(0)} seconds...`);

        return;
    }
}

world.afterEvents.entityHitEntity.subscribe((event) => {
    const { damagingEntity, hitEntity } = event;

    if (damagingEntity.typeId === "minecraft:player") {
        damagingEntity.setDynamicProperty("pixell_bbo:no_damage_timeout", 0);
    }

    if (damagingEntity instanceof Player) {
        damagingEntity.setDynamicProperty("pixell_bbo:no_damage_timeout", 0);
    }
    if (hitEntity instanceof Player) {
        hitEntity.setDynamicProperty("pixell_bbo:no_damage_timeout", 0);
    }
});
