import {
    BlockVolume,
    BlockVolumeBase,
    Dimension,
    Entity,
    Player,
    StructureRotation,
    system,
    world,
} from "@minecraft/server";

import { ActionFormData } from "@minecraft/server-ui";

import { resetPlayerGamemode, setPlayerArenaGamemode } from "../entities/Player";
import { getItemAmount, hasItems } from "../entities/Utils";
import {
    getPlayerLastEnteredPortal,
    resetPlayerLastEnteredPortal,
    setPlayerLastEnteredPortal,
} from "../utils/utils";

/**
 * Describes an item requirement to enter or trigger an arena.
 * @typedef {Object} ArenaRequirement
 * @property {string} name - Display name of the requirement (e.g., "Souls").
 * @property {string} item - Fully-qualified item identifier (e.g., "pixell_bbo:souls").
 * @property {number} amount - Amount of the item required.
 */

/**
 * Min/max health range for a boss.
 * @typedef {Object} HealthRange
 * @property {number} min - Minimum health value.
 * @property {number} max - Maximum health value.
 */

/**
 * Metadata shown to players about a boss.
 * @typedef {Object} BossInfo
 * @property {string} name - Display name of the boss.
 * @property {HealthRange} health - Health range displayed in the UI.
 * @property {string[]} description - Array of rich-text lines (supports formatting codes).
 */

/**
 * Full configuration for a single arena portal entry.
 * @typedef {Object} ArenaEntry
 * @property {string} bossName - Short, human-readable boss name.
 * @property {string} bossId - Fully-qualified boss entity identifier (e.g., "pixell_bbo:craterus").
 * @property {string} structureId - Structure identifier used to spawn the arena (e.g., "pixell_bbo:ar/craterus").
 * @property {string} sfx - Optional sound effect identifier to play (empty string if unused).
 * @property {number} offsetX - Horizontal offset (in blocks) used by your placement logic.
 * @property {number} height - Arena Height.
 * @property {import("@minecraft/server").Vector3} playerSpawnOffset - player spawn offset.
 * @property {import("@minecraft/server").Vector3} bossSpawnOffset - boss spawn offset.
 * @property {import("@minecraft/server").Vector3} portalSpawnOffset - portal spawn offset.
 * @property {ArenaRequirement[]} requirements - List of item requirements to access the arena.
 * @property {BossInfo} bossInfo - Display and health info for UI/tooltip purposes.
 */

/**
 * Namespaced identifier (e.g., "namespace:name"). Used as keys for the map.
 * @typedef {string} NamespacedId
 */

/**
 * Maps a portal block identifier to its arena configuration.
 * Keys are portal block IDs (e.g., "pixell_bbo:craterus_portal").
 * Values are {@link ArenaEntry} objects describing boss, structure and requirements.
 * @type {Map<NamespacedId, ArenaEntry>}
 */
export const ArenaMap = new Map([
    [
        "pixell_bbo:craterus_portal",
        {
            bossName: "Craterus",
            bossId: "pixell_bbo:craterus",
            structureId: "pixell_bbo:ar/craterus",
            sfx: "",
            offsetX: 1000,
            height: 200,
            playerSpawnOffset: { x: 22, y: 5, z: 0 },
            bossSpawnOffset: { x: -3, y: 4, z: 1 },
            portalSpawnOffset: { x: -3, y: 3, z: 1 },
            requirements: [
                {
                    name: "Souls",
                    item: "pixell_bbo:souls",
                    amount: 112,
                },
            ],
            bossInfo: {
                name: "Craterus",
                health: {
                    min: 1440,
                    max: 2280,
                },
                description: [
                    `
                    
§r§l§2Craterus, the Deep Maw§r

Craterus is a creature of pure rot, its twisted body molded by ages of corruption. 
With jagged fangs and clawed limbs, it strikes without warning. Its fang, left behind,
still hums with restless hunger.
                    `,
                ],
            },
        },
    ],
    [
        "pixell_bbo:ares_portal",
        {
            bossName: "Wrath Gladiator",
            bossId: "pixell_bbo:ares",
            structureId: "pixell_bbo:ar/ares",
            sfx: "",
            offsetX: 5000,
            height: 200,
            playerSpawnOffset: { x: 0, y: 3, z: 25 },
            bossSpawnOffset: { x: 0, y: 1, z: -2 },
            portalSpawnOffset: { x: 0, y: 2, z: -2 },
            requirements: [
                {
                    name: "Souls",
                    item: "pixell_bbo:souls",
                    amount: 80,
                },
            ],
            bossInfo: {
                name: "Wrath Gladiator",
                health: {
                    min: 600,
                    max: 800,
                },
                description: [
                    `

§r§l§3Wrath Gladiator, the Warbringer§r

Clad in cursed steel and wielding blade and axe, Wrath Gladiator is the fury of war made flesh. 
His sword holds the rage of forgotten battles.

                    `,
                ],
            },
        },
    ],
    [
        "pixell_bbo:hades_portal",
        {
            bossName: "Lich",
            bossId: "pixell_bbo:hades",
            structureId: "pixell_bbo:ar/hades",
            sfx: "",
            offsetX: 10000,
            height: 200,
            playerSpawnOffset: { x: 0, y: 3, z: 22 },
            bossSpawnOffset: { x: 0, y: 6, z: 1 },
            portalSpawnOffset: { x: 0, y: 7, z: 1 },
            requirements: [
                {
                    name: "Souls",
                    item: "pixell_bbo:souls",
                    amount: 128,
                },
            ],
            bossInfo: {
                name: "Lich",
                health: {
                    min: 600,
                    max: 800,
                },
                description: [
                    `

§r§l§4Lich, the Nether Regent§r

The royalty of the underworld, goes into battle wielding a twisted scepter.
Lich commands forces beyond the mortal realm. Twisted by power, 
he summons infernal hounds to hunt his foes. His scepter, left behind, 
pulses with unholy might.

                    `,
                ],
            },
        },
    ],
    [
        "pixell_bbo:unknown_portal",
        {
            bossName: "Unknown",
            bossId: "pixell_bbo:unknown",
            structureId: "pixell_bbo:ar/unknown",
            sfx: "",
            offsetX: 15000,
            height: 200,
            playerSpawnOffset: { x: 14, y: 6, z: 21 },
            bossSpawnOffset: { x: -7, y: 4, z: -8 },
            portalSpawnOffset: { x: -7, y: 2, z: -8 },
            requirements: [
                {
                    name: "Souls",
                    item: "pixell_bbo:souls",
                    amount: 48,
                },
            ],
            bossInfo: {
                name: "Unknown",
                health: {
                    min: 320,
                    max: 600,
                },
                description: [
                    `

§r§l§8Unknown, the First Shadow§r

A face-less, unstable shadow born before time. It barely holds itself in this world, 
drawn to fear and emptiness. Its dropped scythe is a relic from the void, echoing its chaotic origin.

                    `,
                ],
            },
        },
    ],
    [
        "pixell_bbo:burazazu_portal",
        {
            bossName: "Burazazu",
            bossId: "pixell_bbo:burazazu",
            structureId: "pixell_bbo:ar/burazazu",
            sfx: "",
            offsetX: 20000,
            height: 200,
            playerSpawnOffset: { x: 0, y: 2, z: 22 },
            bossSpawnOffset: { x: 0, y: 0, z: 0 },
            portalSpawnOffset: { x: 0, y: 2, z: 0 },
            requirements: [
                {
                    name: "Souls",
                    item: "pixell_bbo:souls",
                    amount: 64,
                },
            ],
            bossInfo: {
                name: "Burazazu",
                health: {
                    min: 1440,
                    max: 2280,
                },
                description: [
                    `

§r§l§gBurazazu, the Dragon Samurai§r

Burazazu, a cursed samurai, wears dark armor and a wide-brimmed helmet, 
hiding a face scarred by trials. His katana, forged from dragon essence, 
reflects his honor. He can transform into a dragon, unleashing lightning strikes, 
while seeking redemption for his dual nature.
                    `,
                ],
            },
        },
    ],
    [
        "pixell_bbo:sweetgabriel_portal",
        {
            bossName: "Sky Guardian",
            bossId: "pixell_bbo:sweetgabriel",
            structureId: "pixell_bbo:ar/sweetgabriel",
            sfx: "",
            offsetX: 25000,
            height: 200,
            playerSpawnOffset: { x: 0, y: 3, z: 21 },
            bossSpawnOffset: { x: 1, y: 8, z: 0 },
            portalSpawnOffset: { x: 0, y: 7, z: 0 },
            requirements: [
                {
                    name: "Souls",
                    item: "pixell_bbo:souls",
                    amount: 96,
                },
            ],
            bossInfo: {
                name: "Sky Guardian",
                health: {
                    min: 1440,
                    max: 2280,
                },
                description: [
                    `

§r§l§eSky Guardian, the Sky Herald§r

Formed of endless wings and watching eyes, Sky Guardian drifts silently between realms. 
Neither flesh nor machine, it speaks in visions and strikes with light. Its wings, left behind in grace or judgment, 
grant the gift of flight to those deemed worthy. It lingers just beyond reach, making direct confrontations perilous.

                    `,
                ],
            },
        },
    ],
]);

/**
 *
 * @param {Player} player
 * @param {Entity} portal
 * @param {string} arena
 */

export function portalActivation(player, portal, arena) {
    const isActivated = portal.getProperty("self:activated");

    if (isActivated) {
        return;
    }

    const ArenaData = ArenaMap.get(arena);

    if (!ArenaData) return;

    const { requirements, bossName, bossInfo } = ArenaData;

    const hasRequirements = requirements.every((req) => hasItems(player, req.item, req.amount));

    const requrimentItems = requirements
        .map((req) => {
            const playerItemAmount = getItemAmount(player, req.item);

            if (playerItemAmount >= req.amount) {
                return `§2${req.name}: ${playerItemAmount}/${req.amount}`;
            }

            return `§4${req.name}: ${playerItemAmount}/${req.amount}`;
        })
        .toString()
        .replace(",", " ");

    const requirementsText = `
    Requirements:
    ${requrimentItems}
    `.padEnd(460, " ");

    const bodyText = requirementsText;

    const UI = new ActionFormData();

    UI.title(`${bossName} Portal`);

    UI.body(bodyText);

    UI.button(hasRequirements ? "Activate" : "Close");

    UI.show(player).then((response) => {
        const value = response.selection;

        if (
            response.canceled ||
            response.cancelationReason == "UserClosed" ||
            response.cancelationReason == "UserBusy"
        ) {
            return;
        }

        switch (value) {
            case 0: {
                if (hasRequirements) {
                    for (let item of requirements) {
                        const { item: itemId, name, amount } = item;

                        player.runCommand("clear @s " + itemId + " 0 " + amount);
                    }

                    portal.setProperty("self:activated", true);
                    portal.dimension.playSound("pixell_bbo.portal.activate", portal.location, {
                        pitch: 1.0,
                        volume: 0.5,
                    });
                    portal.dimension.playSound("pixell_bbo.portal.on", portal.location);

                    portalEnter(player, portal, arena);
                }

                break;
            }

            default: {
                break;
            }
        }
    });
}

/**
 *
 * @param {Player} player
 * @param {Entity} portal
 * @param {string} arena
 */

export function portalEnter(player, portal, arena) {
    const ArenaData = ArenaMap.get(arena);

    if (!ArenaData) return;

    const { requirements, bossName, bossInfo } = ArenaData;

    const bodyText = `${bossInfo.description[0]}`.padEnd(300, " ");

    const UI = new ActionFormData();

    UI.title(`${bossName} Portal`);

    UI.body(bodyText);

    UI.button("Enter");

    UI.show(player).then((response) => {
        const value = response.selection;

        if (
            response.canceled ||
            response.cancelationReason == "UserClosed" ||
            response.cancelationReason == "UserBusy"
        ) {
            return;
        }

        switch (value) {
            case 0: {
                //  portal.setProperty("self:enter", true);

                portal.dimension.playSound("pixell_bbo.portal.teleport", portal.location, {
                    pitch: 1.0,
                    volume: 0.5,
                });

                player.addTag("pixell_bbo:teleporting_to_arena");

                system.waitTicks(2 * 20).then(() => {
                    buildArena(player, ArenaData);
                    setPlayerLastEnteredPortal(player, portal);
                });

                break;
            }

            default: {
                break;
            }
        }
    });
}

/**
 *
 * @param {Player} player
 * @param {string} arena
 * @returns
 */
export function portalReturn(player, arena) {
    const lastPortal = getPlayerLastEnteredPortal(player) ?? player.getSpawnPoint();

    const arenaData = ArenaMap.get(arena);

    if (!arenaData) return;

    const { offsetX, height, structureId, playerSpawnOffset, bossSpawnOffset } = arenaData;

    if (!structureId || structureId == "") return;

    const arenaSize = world.structureManager.get(structureId).size;

    const arenaCenter = {
        x: offsetX + arenaSize.x / 2,
        y: height,
        z: offsetX + arenaSize.z / 2,
    };

    const bodyText = "";

    const UI = new ActionFormData();

    UI.title(`Portal`);

    UI.body("");

    UI.button("Return");

    UI.show(player).then((response) => {
        const value = response.selection;

        if (
            response.canceled ||
            response.cancelationReason == "UserClosed" ||
            response.cancelationReason == "UserBusy"
        ) {
            //  player.setDynamicProperty(portalShowPropName, false);
            return;
        }

        switch (value) {
            case 0: {
                player.setDynamicProperty("ninja:portal_ui_show", true);

                system
                    .waitTicks(1 * 20)
                    .then(() => {
                        player.teleport(
                            {
                                x: lastPortal.x - 2,
                                y: lastPortal.y + 2,
                                z: lastPortal.z - 2,
                            },
                            {
                                dimension: world.getDimension("minecraft:overworld"),
                            }
                        );

                        resetPlayerGamemode(player);

                        loadLocation(
                            world.getDimension("the_end"),
                            arena,
                            new BlockVolume(
                                {
                                    x: arenaCenter.x - 32,
                                    y: arenaCenter.y - 32,
                                    z: arenaCenter.z - 32,
                                },
                                {
                                    x: arenaCenter.x + 32,
                                    y: arenaCenter.y + 32,
                                    z: arenaCenter.z + 32,
                                }
                            ),
                            10
                        );
                    })
                    .catch((e) => {
                        console.error(e);
                    });

                system
                    .waitTicks(2 * 20)
                    .then(() => {
                        const hasPlayersInside = world.getDimension("the_end").getPlayers({
                            location: arenaCenter,
                            maxDistance: 64,
                        });

                        if (hasPlayersInside.length == 0) {
                            system
                                .waitTicks(1 * 20)
                                .then(() => {
                                    const portalLoc = getPlayerLastEnteredPortal(player);

                                    if (!portalLoc) {
                                        resetPlayerLastEnteredPortal(player);
                                        return;
                                    }

                                    const portalType = world
                                        .getDimension("overworld")
                                        .getEntities({
                                            location: portalLoc,
                                            maxDistance: 3,
                                            type: "pixell_bbo:portal",
                                            closest: 1,
                                        })[0]
                                        .getProperty("self:portal_type");

                                    const portals = world.getDimension("overworld").getEntities({
                                        location: portalLoc,
                                        type: "pixell_bbo:portal",
                                    });

                                    if (portals.length > 0) {
                                        for (let portal of portals) {
                                            if (
                                                portal.getProperty("self:portal_type") != portalType
                                            )
                                                return;

                                            portal.remove();
                                        }

                                        resetPlayerLastEnteredPortal(player);
                                    }
                                })
                                .catch((e) => {
                                    console.error(e);
                                });
                        }
                    })
                    .catch((e) => {
                        console.error(e);
                    });

                break;
            }

            default: {
                break;
            }
        }
    });
}

/**
 *
 * @param {Player} player
 * @param {ArenaEntry} arenaData
 */
export function buildArena(player, arenaData) {
    player.onScreenDisplay.setActionBar("§bTeleporting...");

    const { offsetX, height, bossName, structureId, playerSpawnOffset, bossSpawnOffset, bossId } =
        arenaData;

    if (!structureId || structureId == "") return;

    const arenaSize = world.structureManager.get(structureId).size;

    const arenaCenter = {
        x: offsetX + arenaSize.x / 2,
        y: height,
        z: offsetX + arenaSize.z / 2,
    };

    const bossSpawnLoc = {
        x: arenaCenter.x + bossSpawnOffset.x,
        y: arenaCenter.y + bossSpawnOffset.y,
        z: arenaCenter.z + bossSpawnOffset.z,
    };

    const teleportingPlayers = world
        .getPlayers({
            tags: ["pixell_bbo:teleporting_to_arena"],
        })
        .filter((p) => p.id !== player.id);

    const delayBetweenPlayersTeleport = 3;

    loadLocation(
        world.getDimension("the_end"),
        arenaData.bossId,
        new BlockVolume(
            {
                x: offsetX - 64,
                y: height - 16,
                z: offsetX - 64,
            },
            {
                x: offsetX + 64,
                y: height + 64,
                z: offsetX + 64,
            }
        ),
        5
    );

    const hasPlayersInside = world.getDimension("the_end").getPlayers({
        location: arenaCenter,
        maxDistance: 64,
    });

    const isBossAlive = world.getDimension("the_end").getEntities({
        location: arenaCenter,
        families: ["boss"],
        maxDistance: 64,
    });

    if (hasPlayersInside.length > 0 || isBossAlive.length > 0) {
        player.camera.fade({
            fadeColor: { red: 0, green: 0, blue: 0 },
            fadeTime: {
                fadeInTime: 1,
                holdTime: 4,
                fadeOutTime: 2,
            },
        });

        system
            .waitTicks(2 * 20)
            .then(() => {
                const spawnLoc = {
                    x: arenaCenter.x + playerSpawnOffset.x,
                    y: arenaCenter.y + playerSpawnOffset.y,
                    z: arenaCenter.z + playerSpawnOffset.z,
                };

                const spawnProtectionDuration = 20 * 20;

                player.teleport(spawnLoc, {
                    dimension: world.getDimension("the_end"),
                });

                player.addEffect("resistance", spawnProtectionDuration, {
                    amplifier: 255,
                    showParticles: false,
                });

                player.addEffect("weakness", spawnProtectionDuration, {
                    amplifier: 255,
                    showParticles: false,
                });

                player.addEffect("invisibility", spawnProtectionDuration, {
                    amplifier: 255,
                    showParticles: false,
                });

                player.setDynamicProperty(
                    "pixel_studios::player::spawn_protection",
                    spawnProtectionDuration
                );

                setPlayerArenaGamemode(player);

                player.removeTag("pixell_bbo:teleporting_to_arena");
            })
            .catch((e) => {
                console.error(e);
            });
        return;
    }

    if (teleportingPlayers.length > 0) {
        player.onScreenDisplay.setActionBar("§bPortal in use, wait...");

        player.removeTag("pixell_bbo:teleporting_to_arena");

        return;
    }

    system.waitTicks(1 * 20).then(() => {
        loadLocation(
            world.getDimension("the_end"),
            arenaData.bossId,
            new BlockVolume(
                {
                    x: offsetX - 64,
                    y: height - 16,
                    z: offsetX - 64,
                },
                {
                    x: offsetX + 64,
                    y: height + 64,
                    z: offsetX + 64,
                }
            ),
            20
        );

        //
    });

    system.waitTicks(1.5 * 20).then(() => {
        player.camera.fade({
            fadeColor: { red: 0, green: 0, blue: 0 },
            fadeTime: {
                fadeInTime: 1,
                holdTime: 4,
                fadeOutTime: 2,
            },
        });

        const entities = world.getDimension("the_end").getEntities({
            location: arenaCenter,
            maxDistance: 128,
        });

        for (let entity of entities) {
            if (entity instanceof Player) continue;

            entity.remove();
        }

        system.waitTicks(1 * 20).then(() => {
            world.structureManager.place(
                structureId,
                world.getDimension("the_end"),
                {
                    x: offsetX,
                    y: height,
                    z: offsetX,
                },
                {
                    rotation: StructureRotation.None,
                }
            );
        });

        //
    });

    system
        .waitTicks(5 * 20)
        .then(() => {
            const spawnLoc = {
                x: arenaCenter.x + playerSpawnOffset.x,
                y: arenaCenter.y + playerSpawnOffset.y,
                z: arenaCenter.z + playerSpawnOffset.z,
            };

            player.teleport(spawnLoc, {
                dimension: world.getDimension("the_end"),
            });

            player.removeTag("pixell_bbo:teleporting_to_arena");

            setPlayerArenaGamemode(player);

            //
        })
        .then(() => {
            const spawnLoc = {
                x: arenaCenter.x + bossSpawnOffset.x,
                y: arenaCenter.y + bossSpawnOffset.y,
                z: arenaCenter.z + bossSpawnOffset.z,
            };

            loadLocation(
                world.getDimension("the_end"),
                arenaData.bossId,
                new BlockVolume(
                    {
                        x: spawnLoc.x - 64,
                        y: spawnLoc.y - 16,
                        z: spawnLoc.x - 64,
                    },
                    {
                        x: spawnLoc.x + 64,
                        y: spawnLoc.y + 64,
                        z: spawnLoc.x + 64,
                    }
                ),
                2
            );

            system.waitTicks(2 * 20).then(() => {
                const boss = player.dimension.spawnEntity(bossId, spawnLoc);

                const skybox = world
                    .getDimension("the_end")
                    .spawnEntity("pixell_bbo:skybox", bossSpawnLoc);

                boss.addTag("pixell_bbo:boss");
            });

            //
        })
        .catch((e) => {
            console.error(e);
        });
}

/**
 *
 * @param {Dimension} dimension
 * @param {string} name
 * @param {BlockVolumeBase | BlockVolume} area
 */
export function loadLocation(dimension, name, area, loadDuration = 5) {
    //dimension.runCommand(`tickingarea remove ${name}`);

    const min = area.getMin();
    const max = area.getMax();

    dimension.runCommand(
        `tickingarea add ${min.x} ${min.y} ${min.z} ${max.x} ${max.y} ${max.z} ${name}`
    );

    system.waitTicks(loadDuration * 20).then(() => {
        dimension.runCommand(`tickingarea remove ${name}`);
    });
}

/**
 *
 * @param {player} player
 * @param {ArenaEntry} arenaData
 */
export function build(player, arenaData) {}
