import { world, system } from "@minecraft/server";
import { MessageFormData } from "@minecraft/server-ui";
import * as utils from "./utils";
import * as Vector3 from "./vector3";
import * as portal from "./portal";

const vanillaMobs = [
    "minecraft:zombie",
    "minecraft:skeleton",
    "minecraft:creeper",
    "minecraft:cave_spider",
    "minecraft:spider",
    "minecraft:slime",
    "minecraft:witch",
    "minecraft:enderman"
]

const dungeonData = { 
    "goe_bos:spider_nest": {
        title: "§5Spider's Nest",
        fog: "goe_bos:spiders_nest_fog", 
        music: { name: "goe_bos:spider_dungeon_bg", duration: 380 },
        bossMusic: { name: "goe_bos:spider_queen_music", duration: 2440 },
        bottomOffset: { x: -5, y: -37, z: -6 },
        entrance: { offset: { x: 0, y: 12, z: 32 }, rotation: 180 }, 
		spawnOffset: { x: 0, y: 11, z: 27.5 },
        exit: { offset: { x: 2, y: -29, z: -10 }, rotation: 0 },
        loadDuration: 30,
        colorFade: { red: 0.18, green: 0.02, blue: 0.18 }
    },
    "goe_bos:piglins_lair": {
        title: "§6Piglin's Lair", 
        fog: "goe_bos:piglins_lair_fog", 
        music: { name: "goe_bos:piglins_lair_bg", duration: 1260 },
        bossMusic: { name: "goe_bos:hoglin_warlord_music", duration: 5080 },
        bottomOffset: { x: -7, y: -55, z: -10 },
        entrance: { offset: { x: 0, y: 27, z: 27 }, rotation: 180 }, 
		spawnOffset: { x: 0, y: 26, z: 22.5 },
        exit: { offset: { x: -0.5, y: -29, z: 5 }, rotation: 180 },
        loadDuration: 30,
        colorFade: { red: 0.55, green: 0.25, blue: 0 }
    },
    "goe_bos:haunted_tomb": {
        title: "§7Haunted Tomb", 
        fog: "goe_bos:haunted_tomb_fog", 
        music: { name: "goe_bos:haunted_tomb_bg", duration: 3040 },
        bossMusic: { name: "goe_bos:skeleton_king_music", duration: 2620 },
        bottomOffset: { x: -1, y: -43, z: -2 },
        entrance: { offset: { x: 5, y: 15, z: 41 }, rotation: 180 }, 
		spawnOffset: { x: 5, y: 15, z: 37 },
        exit: { offset: { x: -3, y: -31, z: 18 }, rotation: 180 },
        loadDuration: 30,
        colorFade: { red: 0.63, green: 0.63, blue: 0.63 }
    },
    "goe_bos:wardens_chamber": {
        title: "§3Warden's Chamber", 
        fog: "goe_bos:wardens_chamber_fog", 
        music: { name: "goe_bos:wardens_chamber_bg", duration: 1100 },
        bossMusic: { name: "goe_bos:warden_king_music", duration: 1760 },
        bottomOffset: { x: -6, y: -47, z: -9 },
        entrance: { offset: { x: 12, y: 18, z: 34 }, rotation: 180 }, 
		spawnOffset: { x: 12, y: 17, z: 30 },
        exit: { offset: { x: 21, y: -29, z: 3 }, rotation: 90 },
        loadDuration: 30,
        colorFade: { red: 0.07, green: 0.22, blue: 0.26 }
    },
    "goe_bos:necromancer_ruins": {
        title: "§aNecromancer Ruins", 
        fog: "goe_bos:unholy_ruins_fog", 
        music: { name: "goe_bos:wardens_chamber_bg", duration: 1100 },
        bossMusic: { name: "goe_bos:necromancer_music", duration: 2020 },
        bottomOffset: { x: -2, y: -42, z: -5 },
        entrance: { offset: { x: 0, y: 14, z: 25 }, rotation: 180 }, 
		spawnOffset: { x: 0, y: 14, z: 21 },
        exit: { offset: { x: 1, y: -28, z: 5 }, rotation: -180 },
        loadDuration: 30,
        colorFade: { red: 0.6, green: 1.0, blue: 0.3 }
    },
    "goe_bos:frostbite_cavern": {
        title: "§bFrostbite Cavern", 
        fog: "goe_bos:frostbite_cavern_fog",
        music: { name: "goe_bos:wardens_chamber_bg", duration: 1100 },
        bossMusic: { name: "goe_bos:necromancer_music", duration: 2020 },
        bottomOffset: { x: -7, y: -60, z: -10 },
        entrance: { offset: { x: 0, y: 21, z: 32 }, rotation: 180 }, 
		spawnOffset: { x: 0, y: 21, z: 28 },
        exit: { offset: { x: -6.5, y: -38, z: 0.5 }, rotation: -90 },
        loadDuration: 30,
        colorFade: { red: 0.0, green: 1.0, blue: 1.0 }
    },
    "goe_bos:forbidden_crypt": {
        title: "§2Forbidden Crypt", 
        fog: "goe_bos:forbidden_crypt_fog", 
        music: { name: "goe_bos:wardens_chamber_bg", duration: 1100 },
        bossMusic: { name: "goe_bos:wither_wizard_music", duration: 1660 },
        bottomOffset: { x: -5, y: -51, z: -4 },
        entrance: { offset: { x: -4, y: 32, z: 33 }, rotation: 180 }, 
		spawnOffset: { x: -4, y: 32, z: 29 },
        exit: { offset: { x: 4, y: -23, z: -2 }, rotation: 90 },
        loadDuration: 30,
        colorFade: { red: 0.0, green: 0.48, blue: 0.4 }
    },
    "goe_bos:void_sanctum": {
        title: "§dVoid Sanctum", 
        fog: "goe_bos:void_sanctum_fog",
        music: { name: "goe_bos:wardens_chamber_bg", duration: 1100 },
        bossMusic: { name: "goe_bos:void_abomination_music", duration: 2240 },
        bottomOffset: { x: 0, y: -43, z: -7 },
        entrance: { offset: { x: 1, y: 16, z: 35 }, rotation: 180 }, 
		spawnOffset: { x: 1, y: 16, z: 31 },
        exit: { offset: { x: 1, y: -27, z: 2 }, rotation: 180 },
        loadDuration: 30,
        colorFade: { red: 1.0, green: 0.5, blue: 0.92 }
    }
}

const music = {};

export async function loadDungeon(dungeonId, overworldPortal) {
    // Calculate dungeon corners
    let dungeonNum = await utils.advanceWorldParam("goe_bos_dungeon_num");
	let taStart = Vector3.copy({ x: 10000 + dungeonNum*250, y: 0, z: 15000 });
	let taEnd = taStart.add({x: 64, y: 0, z: 64});
	let dungeonLocation = taStart.add({x: 0, y: 80, z: 0});
	
    // Create ticking area in end dimension
	utils.runCommand(`tickingarea add ${dungeonLocation.x - 64} ${dungeonLocation.y} ${dungeonLocation.z - 64} ${taEnd.toCommandInt()} goe_bos_${dungeonNum}`, utils.the_end);

	// Wait 1s for ticking area to be loaded before loading structure
	system.runTimeout(() => loadDungeonStep2(dungeonId, dungeonLocation, dungeonNum, overworldPortal), 20);
}

export async function loadDungeonStep2(dungeonId, dungeonLocation, dungeonNum, overworldPortal) {
    let bottomLocation = dungeonLocation.add(dungeonData[dungeonId].bottomOffset);

    // Load dungeon structure
    await utils.runCommand(`structure load ${dungeonId} ${dungeonLocation.toCommand()}`, utils.the_end);
    system.runTimeout(() => utils.runCommand(`structure load ${dungeonId}_bottom ${bottomLocation.toCommand()}`, utils.the_end), 10);

    // Wait 1s for structure for structure to load
	system.runTimeout(async () => loadDungeonStep3(dungeonId, dungeonLocation, dungeonNum, overworldPortal), 60);
}

export async function loadDungeonStep3(dungeonId, dungeonLocation, dungeonNum, overworldPortal) {
    let dungeonEntity = undefined;
    while (dungeonEntity === undefined) {
        // Get dungeon entity
        dungeonEntity = utils.the_end.getEntities({
            location: dungeonLocation,
            type: "goe_bos:dungeon",
            closest: 1,
            maxDistance: 60
        })[0];
    }

    // utils.debug("Structure loaded");
    // utils.debug("Dungeon: " + dungeonEntity.location.x.toFixed(2) + " " + dungeonEntity.location.y.toFixed(2) + " " + dungeonEntity.location.z.toFixed(2));
    
    // Calcualte player spawn location
    const spawnLocation = Vector3.copy(dungeonEntity.location).add(dungeonData[dungeonId].spawnOffset).toLocation();
    // utils.debug("Spawn location: " + spawnLocation.x.toFixed(2) + " " + spawnLocation.y.toFixed(2) + " " + spawnLocation.z.toFixed(2));
    overworldPortal.setDynamicProperty("goe_bos_tp_location", spawnLocation);
    overworldPortal.setDynamicProperty("goe_bos_tp_rotation", dungeonData[dungeonId].entrance.rotation);
	overworldPortal.setDynamicProperty("goe_bos_portal_is_active", true);
	overworldPortal.addTag("goe_bos_portal_is_active");

    // Calculate entrance portal location
    const entranceLocation = Vector3.copy(dungeonEntity.location).add(dungeonData[dungeonId].entrance.offset).toLocation();

    // Spawn entrance portal
    await portal.placeExitPortal(dungeonId, entranceLocation, dungeonData[dungeonId].entrance.rotation, spawnLocation);

    // Remove ticking area
    await utils.runCommand(`tickingarea remove goe_bos_${dungeonNum}`, utils.the_end);
}

export async function onTick() {
    for (const player of world.getAllPlayers()) {
        if (!player.hasTag("goe_bos_in_dungeon") || player.hasTag("goe_bos_dungeon_finished"))
            continue;

        updateDungeonMusic(player);
        // system.runTimeout(() => updateBossMusic(player), 5);

        if (player.hasTag("goe_bos_spawned")) {
            // utils.runTimeout(() =>  displayDungeonTitle(player), 100);
            await startDungeonFog(player);
            player.removeTag("goe_bos_spawned")
        }
            
    }
}

export async function onEntitySpawn(event) {
    let entity = event.entity;

    if (entity === undefined)
        return;
    
    if(vanillaMobs.includes(entity.typeId)){
        try{
            entity.addTag("goe_bos_spawned");
        } catch {

        }
    }
}

export async function onEntityDie(event) {
    const player = event.deadEntity;
    // clearPlayerDungeonEffects(player);
    player.removeTag("goe_bos_in_dungeon");
}

export async function onPlayerSpawn(event) {
    await clearPlayerDungeonEffects(event.player);
    event.player.addTag("goe_bos_spawned");
}

export async function startDungeonFog(player) {
    const dungeonId = player.getDynamicProperty("goe_bos_dungeon_id");
    const dungeon = dungeonData[dungeonId];
    utils.runPlayerCommand(player, `fog @s push ${dungeon.fog} ${dungeon.fog}`);
}

export async function displayDungeonTitle(player) {
    const dungeonId = player.getDynamicProperty("goe_bos_dungeon_id");
    const dungeon = dungeonData[dungeonId];
    utils.runPlayerCommand(player, "playsound goe_bos:title_appear @s");

    system.runTimeout(() => player.onScreenDisplay.setTitle(`§l＀${dungeon.title}`, { /*subtitle: `＀${dungeon.subtitle}`,*/ fadeInDuration: 20, stayDuration: 60, fadeOutDuration: 10 }), 1);
    // player.onScreenDisplay.setTitle(`§l＀${dungeon.title}`, { fadeInDuration: 20, stayDuration: 60, fadeOutDuration: 10 });
}

export async function updateDungeonMusic(player) {
    if (!player.hasTag("goe_bos_boss_music")) {
        const dungeonEntity = await utils.getEntityNearEntity("goe_bos:dungeon", player);
		if (dungeonEntity && dungeonEntity.isValid() && dungeonEntity.hasTag("goe_bos_boss_music")) {
            player.addTag("goe_bos_boss_music");

            // Stop background music
            if (music[player.id]) {
                utils.runPlayerCommand(player, `stopsound @s ${music[player.id].music.name}`);
                delete  music[player.id];
            }
        }
    }

    if (!music[player.id]) {
        const dungeonId = player.getDynamicProperty("goe_bos_dungeon_id");
        const dungeon = dungeonData[dungeonId];
        if (!dungeon) return;

        const musicToPlay = !player.hasTag("goe_bos_boss_music") ? dungeon.music : dungeon.bossMusic;
        music[player.id] = { music: musicToPlay, timePassed: 0 }
        player.playSound(musicToPlay.name);
        return;
    } 

    if (++music[player.id].timePassed >= music[player.id].music.duration) {
        music[player.id].timePassed = 0;
        player.playSound(music[player.id].music.name);
    }
}

export async function clearPlayerDungeonEffects(player)
{
    utils.runPlayerCommand(player, "stopsound @s");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:spiders_nest_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:piglins_lair_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:haunted_tomb_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:wardens_chamber_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:unholy_ruins_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:frostbite_cavern_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:forbidden_crypt_fog");
    utils.runPlayerCommand(player, "fog @s remove goe_bos:void_sanctum_fog");

    delete music[player.id];

    if(player.hasTag("goe_bos_boss_music"))
        player.removeTag("goe_bos_boss_music");

    if(player.hasTag("spider_nest"))
        player.removeTag("spider_nest");

    if(player.hasTag("piglins_lair"))
        player.removeTag("piglins_lair");

    if(player.hasTag("haunted_tomb"))
        player.removeTag("haunted_tomb");

    if(player.hasTag("wardens_chamber"))
        player.removeTag("wardens_chamber");

    if(player.hasTag("unholy_ruins"))
        player.removeTag("unholy_ruins");

    if(player.hasTag("frostbite_cavern"))
        player.removeTag("frostbite_cavern");

    if(player.hasTag("forbidden_crypt"))
        player.removeTag("forbidden_crypt");

    if(player.hasTag("void_sanctum"))
        player.removeTag("void_sanctum");

    if(player.hasTag("goe_bos_title"))
        player.removeTag("goe_bos_title");

    if(player.hasTag("goe_bos_dungeon_finished"))
        player.removeTag("goe_bos_dungeon_finished");

    // Reset spawn point
    const spawnPoint = player.getDynamicProperty("goe_bos_spawn_point");
	if (!spawnPoint) return;

	player.setSpawnPoint({
		dimension: utils.overworld,
		x: spawnPoint.x,
		y: spawnPoint.y,
		z: spawnPoint.z
	});
}

export async function onScriptEventReceive(event) {
	const entity = event.sourceEntity;

    if (event.message === "FINISHED") {
        utils.debug("§6Collect you reward!\n§aA portal has appeared in the boss room.\n§eStep into the portal to exit the dungeon.");
        await spawnExitPortal(entity);
    } else if (event.message === "TITLE") {
        await displayDungeonTitle(entity);
    }

}

async function spawnExitPortal(player) {
    const dungeonId = player.getDynamicProperty("goe_bos_dungeon_id");
    if (!dungeonId) return;
    
    const dungeon = dungeonData[dungeonId];

	const dungeonEntity = player.dimension.getEntities({
		type: "goe_bos:dungeon",
		location: player.location,
		closest: 1
	})[0];

    if (!dungeonEntity || !dungeonEntity.isValid()) return;

    dungeonEntity.addTag("goe_bos_dungeon_finished");
    player.addTag("goe_bos_dungeon_finished");
    
    const exitLocation = {
        x: dungeonEntity.location.x + dungeon.exit.offset.x,
        y: dungeonEntity.location.y + dungeon.exit.offset.y,
        z: dungeonEntity.location.z + dungeon.exit.offset.z
    };

    portal.placeExitPortal(dungeonId, exitLocation, dungeon.exit.rotation, player.getDynamicProperty("goe_bos_tp_back"));
}

export async function enterCameraFade(player, dungeonId) {
    player.camera.fade({
        fadeColor: dungeonData[dungeonId].colorFade, 
        fadeTime: { fadeInTime: 1, holdTime: 2, fadeOutTime: 0.5 }
    });
}

export async function exitCameraFade(player) {
    player.camera.fade({
        fadeColor: { red: 0.07, green: 0.22, blue: 0.26 }, 
        fadeTime: { fadeInTime: 1, holdTime: 1.5, fadeOutTime: 0.5 }
    });
}

export async function deleteDungeon(dungeonEntity) {
	// Remove portals
	const portals = utils.the_end.getEntities({
		type: "goe_bos:portal",
		location: dungeonEntity.location,
		maxDistance: 50
	});

	portals.forEach(portal => portal.remove());

	// Remove dungeon entity
	dungeonEntity.remove();
}

export async function displayExitForm(player, targetPortal) {
    if (player.dimension.id !== "minecraft:the_end") {
        portal.teleportPlayer(player, targetPortal);
        return;
    }

    const dungeonEntity = player.dimension.getEntities({
		type: "goe_bos:dungeon",
		location: player.location,
		closest: 1
	})[0];

    if (!dungeonEntity || !dungeonEntity.isValid()) return;

    const messageForm = new MessageFormData()
        .title("Leave dungeon?")
        .body("Are you sure you want to leave?\n")
        .button1("Stay")
        .button2("Leave");

    messageForm.show(player).then(formData => {
        if (formData.canceled || formData.selection === undefined) {
            return;
        }

        if (formData.selection == 1)
            portal.teleportPlayer(player, targetPortal);

    }).catch(error => {
    });
}