import { world, system } from "@minecraft/server";
import { fo } from './utilities/fo_constants.js';



const MIRROR_DATA_KEY = "mirrorCaveData";
const playerStates = new Map();
let eventFlag = false;


function isPlayerAlone(player, radius = 64) {
    const nearbyPlayers = player.dimension.getPlayers({
        location: player.location,
        maxDistance: radius
    });
    return nearbyPlayers.length === 1; 
}

function isInCaveYLevel(player) {
    const y = Math.floor(player.location.y);
    return y >= -20 && y <= 10;
}

function getMirrorLocation(location) {
    return {
        x: -location.x,
        y: location.y,
        z: -location.z
    };
}

function isMirrorAreaClear(player, mirrorLocation, radius = 64) {
    const nearbyPlayers = player.dimension.getPlayers({
        location: mirrorLocation,
        maxDistance: radius
    });
    return nearbyPlayers.length === 0;
}


function saveAreaStructure(dimension, center, size, structureName) {
    const halfSize = size / 2;
    const start = {
        x: Math.floor(center.x - halfSize),
        y: Math.floor(center.y - halfSize),
        z: Math.floor(center.z - halfSize)
    };
    const end = {
        x: Math.floor(center.x + halfSize),
        y: Math.floor(center.y + halfSize),
        z: Math.floor(center.z + halfSize)
    };

    try {
        world.structureManager.createFromWorld(
            structureName,
            dimension,
            start,
            end,
            {
                saveMode: "World",
                includeEntities: false,
                includeBlocks: true
            }
        );
        return { start, end, structureName };
    } catch (e) {
        console.error("Failed to save structure:", e);
        return null;
    }
}

function placeStructure(dimension, location, structureName) {
    try {
        world.structureManager.place(
            structureName,
            dimension,
            location,
            {
                animationMode: "None"
            }
        );
        return true;
    } catch (e) {
        console.error("Failed to place structure:", e);
        return false;
    }
}

function createTickingArea(location, size, name) {
    const halfSize = size / 2;
    const start = {
        x: Math.floor(location.x - halfSize),
        y: Math.floor(location.y - halfSize),
        z: Math.floor(location.z - halfSize)
    };
    const end = {
        x: Math.floor(location.x + halfSize),
        y: Math.floor(location.y + halfSize),
        z: Math.floor(location.z + halfSize)
    };

    try {
        fo.overworld.runCommand(`tickingarea add ${start.x} ${start.y} ${start.z} ${end.x} ${end.y} ${end.z} ${name}`);
        return true;
    } catch (e) {
        console.error("Failed to create ticking area:", e);
        return false;
    }
}

function cleanupStructures(structureNames) {
    structureNames.forEach(name => {
        try {
            world.structureManager.delete(name);
        } catch (e) {
            
        }
    });
}


function startIdleDetection(player, callback, timeoutTicks = 100) { 
    const playerId = player.id;

    
    playerStates.set(playerId, {
        lastLocation: player.location,
        idleTimeout: system.runTimeout(() => {
            callback(player);
        }, timeoutTicks)
    });
}

function resetIdleDetection(player) {
    const playerId = player.id;
    const state = playerStates.get(playerId);

    if (state && state.idleTimeout) {
        system.clearRun(state.idleTimeout);
    }

    playerStates.set(playerId, {
        lastLocation: player.location,
        idleTimeout: system.runTimeout(() => {
            checkMirrorConditions(player);
        }, 100) 
    });
}

function clearPlayerState(playerId) {
    const state = playerStates.get(playerId);
    if (state && state.idleTimeout) {
        system.clearRun(state.idleTimeout);
    }
    playerStates.delete(playerId);
}


function checkMirrorConditions(player) {
    if (!isPlayerAlone(player) || !isInCaveYLevel(player)) {
        return;
    }

    const mirrorLocation = getMirrorLocation(player.location);

    if (!isMirrorAreaClear(player, mirrorLocation)) {
        return;
    }

    
    system.runTimeout(() => { initiateMirrorProcess(player, mirrorLocation) }, 1);
}

function initiateMirrorProcess(player, mirrorLocation) {
    const playerId = player.id;
    const areaSize = 32;

    
    const baseName = `fo:mirror${playerId}_${Math.floor(Math.random() * 1000)}`;
    const playerAreaName = `${baseName}_player`;
    const mirrorAreaName = `${baseName}_mirror`;
    const tickingAreaName = `foMirrorCave`;

    
    const playerArea = saveAreaStructure(player.dimension, player.location, areaSize, playerAreaName);
    if (!playerArea) {
        console.error("Failed to save player area");
        return;
    }

    
    const originalMirrorArea = saveAreaStructure(player.dimension, mirrorLocation, areaSize, mirrorAreaName);
    if (!originalMirrorArea) {
        cleanupStructures([playerAreaName]);
        console.error("Failed to save mirror area");
        return;
    }

    
    if (!createTickingArea(mirrorLocation, areaSize + 16, tickingAreaName)) {
        cleanupStructures([playerAreaName, mirrorAreaName]);
        console.error("Failed to create ticking area");
        return;
    }

    
    const mirrorData = {
        playerId: playerId,
        playerArea: playerArea,
        mirrorArea: originalMirrorArea,
        tickingAreaName: tickingAreaName,
        structures: [playerAreaName, mirrorAreaName],
        originalPlayerLocation: player.location,
        mirrorLocation: mirrorLocation,
        state: "waiting_for_teleport"
    };

    
    const globalData = JSON.parse(world.getDynamicProperty(MIRROR_DATA_KEY) || "{}");
    globalData[playerId] = mirrorData;
    world.setDynamicProperty(MIRROR_DATA_KEY, JSON.stringify(globalData));

    eventFlag = true;
    
    system.runTimeout(() => {
        executeMirrorTeleport(player, mirrorData);
    }, 60); 
}

function executeMirrorTeleport(player, mirrorData) {
    const playerId = player.id;

    
    if (!placeStructure(player.dimension, mirrorData.mirrorArea.start, mirrorData.playerArea.structureName)) {
        cleanupMirrorProcess(playerId);
        return;
    }

    
    const relativeOffset = {
        x: player.location.x - mirrorData.originalPlayerLocation.x,
        y: player.location.y - mirrorData.originalPlayerLocation.y,
        z: player.location.z - mirrorData.originalPlayerLocation.z
    };

    const targetLocation = {
        x: mirrorData.mirrorLocation.x + relativeOffset.x,
        y: mirrorData.mirrorLocation.y + relativeOffset.y,
        z: mirrorData.mirrorLocation.z + relativeOffset.z
    };

    
    system.runTimeout(() => {
        player.teleport(targetLocation, { dimension: player.dimension });

        
        mirrorData.state = "monitoring";
        const globalData = JSON.parse(world.getDynamicProperty(MIRROR_DATA_KEY) || "{}");
        globalData[playerId] = mirrorData;
        world.setDynamicProperty(MIRROR_DATA_KEY, JSON.stringify(globalData));

        
        startMirrorMonitoring(player, mirrorData);
    }, 60)
}

function startMirrorMonitoring(player, mirrorData) {
    const playerId = player.id;
    const monitorInterval = system.runInterval(() => {
        const currentGlobalData = JSON.parse(world.getDynamicProperty(MIRROR_DATA_KEY) || "{}");
        const currentData = currentGlobalData[playerId];

        if (!currentData || currentData.state !== "monitoring") {
            system.clearRun(monitorInterval);
            return;
        }

        const nearbyPlayers = fo.overworld.getPlayers({
            location: mirrorData.mirrorLocation,
            maxDistance: 128,
        })

        if (nearbyPlayers === 0) {
            cleanupMirrorProcess(playerId);
            system.clearRun(monitorInterval);
        }

    }, 20); 
}

function cleanupMirrorProcess(playerId) {
    const globalData = JSON.parse(world.getDynamicProperty(MIRROR_DATA_KEY) || "{}");
    const mirrorData = globalData[playerId];

    if (!mirrorData) return;

    
    if (mirrorData.mirrorArea) {
        placeStructure(fo.overworld, mirrorData.mirrorArea.start, mirrorData.mirrorArea.structureName);
    }

    
    system.runTimeout(() => {
        eventFlag = false;
        if (mirrorData.tickingAreaName) fo.overworld.runCommand(`tickingarea remove ${mirrorData.tickingAreaName}`);

        
        if (mirrorData.structures) {
            cleanupStructures(mirrorData.structures);
        }

        
        delete globalData[playerId];
        world.setDynamicProperty(MIRROR_DATA_KEY, JSON.stringify(globalData));

        
        clearPlayerState(playerId);
    }, 200)
}


system.runInterval(() => {
    if (eventFlag) return;
    for (const player of fo.overworld.getPlayers()) {
        if (!isPlayerAlone(player) || !isInCaveYLevel(player)) {
            resetIdleDetection(player);
            continue;
        }

        const playerId = player.id;
        const state = playerStates.get(playerId);

        if (!state) {
            startIdleDetection(player, checkMirrorConditions);
            continue;
        }

        
        const distanceMoved = Math.sqrt(
            Math.pow(player.location.x - state.lastLocation.x, 2) +
            Math.pow(player.location.z - state.lastLocation.z, 2)
        );

        if (distanceMoved > 1) {
            resetIdleDetection(player);
        }
    }
}, 20); 



system.runTimeout(() => {
    const globalData = JSON.parse(world.getDynamicProperty(MIRROR_DATA_KEY) || "{}");
    for (const playerId in globalData) {
        cleanupMirrorProcess(playerId);
    }
}, 20);