import { Block, Dimension } from "@minecraft/server";

/**
* Recupera los bloques vecinos de un bloque de origen
* @param {Block} block 
* @returns {{
* blockNeighborUp: Block, 
* blockNeighborDown: Block, 
* blockNeighborEast: Block, 
* blockNeighborWest: Block,
* blockNeighborNorth: Block, 
* blockNeighborSouth: Block
* }}
*/
export const blockNeighbors = (block) => {
    try {
        if (!block) return;

        const blockNeighborUp = block.above(1);
        const blockNeighborDown = block.below(1);
        const blockNeighborEast = block.east(1);
        const blockNeighborWest = block.west(1);
        const blockNeighborNorth = block.north(1);
        const blockNeighborSouth = block.south(1);

        return {
            blockNeighborUp,
            blockNeighborDown,
            blockNeighborEast,
            blockNeighborWest,
            blockNeighborNorth,
            blockNeighborSouth
        };
    } catch (error) {
        return {}
    }
}

/**
 * Verifica si algún vecino tiene la etiqueta especificada
 * @param {Block} block
 * @param {string} tag 
 * @returns {boolean}
 */
export const checkAnyNeighborHasTag = (block, tag) => {
    const {
        blockNeighborDown, blockNeighborUp,
        blockNeighborEast, blockNeighborWest,
        blockNeighborNorth, blockNeighborSouth
    } = blockNeighbors(block);

    if(!blockNeighborUp || !blockNeighborDown ||
        !blockNeighborEast || !blockNeighborWest ||
        !blockNeighborNorth || !blockNeighborSouth) return;

    return (
        blockNeighborEast.hasTag(tag) ||
        blockNeighborWest.hasTag(tag) ||
        blockNeighborNorth.hasTag(tag) ||
        blockNeighborSouth.hasTag(tag) ||
        blockNeighborUp.hasTag(tag) ||
        blockNeighborDown.hasTag(tag)
    );
};



/**
 * Encuentra el primer vecino que tiene la etiqueta especificada y no es "nps_ec:waterwheel" (solo aplica a blockNeighborUp y blockNeighborDown)
 * @param {Block} block 
 * @param {string} tag 
 * @returns {Block | null} El primer bloque vecino que tiene la etiqueta especificada y no es "nps_ec:waterwheel", o null si ninguno lo tiene
 */
export const findNeighborWithMatchingTag = (block, tag) => {
    const {
        blockNeighborDown, blockNeighborUp,
        blockNeighborEast, blockNeighborWest,
        blockNeighborNorth, blockNeighborSouth
    } = blockNeighbors(block);

    // Verifica cada vecino y devuelve el primero que tenga la etiqueta y no sea "nps_ec:waterwheel"
    const checkAndReturnValidNeighborUpDown = (neighbor) => {
        return neighbor && neighbor.hasTag(tag) && neighbor.typeId !== "nps_ec:waterwheel" ? neighbor : null;
    };

    const checkAndReturnValidNeighbor = (neighbor) => {
        return neighbor && neighbor.hasTag(tag) ? neighbor : null;
    };

    // Verifica los demás vecinos
    return (
        checkAndReturnValidNeighbor(blockNeighborEast) ||
        checkAndReturnValidNeighbor(blockNeighborWest) ||
        checkAndReturnValidNeighbor(blockNeighborNorth) ||
        checkAndReturnValidNeighbor(blockNeighborSouth) ||
        checkAndReturnValidNeighborUpDown(blockNeighborUp) ||
        checkAndReturnValidNeighborUpDown(blockNeighborDown)
    );
};


/**
 * Verifica si algún vecino es del tipo determinado
 * @param {Block} block
 * @param {string} id 
 * @returns {boolean}
 */
export const checkAnyNeighborHasBlockType = (block, id) => {
    const {
        blockNeighborDown, blockNeighborUp,
        blockNeighborEast, blockNeighborWest,
        blockNeighborNorth, blockNeighborSouth
    } = blockNeighbors(block);

    if(!blockNeighborUp || !blockNeighborDown ||
        !blockNeighborEast || !blockNeighborWest ||
        !blockNeighborNorth || !blockNeighborSouth) return;

    return (
        blockNeighborUp.typeId === id ||
        blockNeighborDown.typeId === id ||
        blockNeighborEast.typeId === id ||
        blockNeighborWest.typeId === id ||
        blockNeighborNorth.typeId === id ||
        blockNeighborSouth.typeId === id
    );
};

/**
 * Verifica si todos los vecinos tiene la etiqueta especificada
 * @param {Block} block
 * @param {string} tag 
 * @returns {boolean}
 */
export const checkAllNeighborHasTag = (block, tag) => {
    const {
        blockNeighborDown, blockNeighborUp,
        blockNeighborEast, blockNeighborWest,
        blockNeighborNorth, blockNeighborSouth
    } = blockNeighbors(block);

    if(!blockNeighborUp || !blockNeighborDown ||
        !blockNeighborEast || !blockNeighborWest ||
        !blockNeighborNorth || !blockNeighborSouth) return;

    return (
        blockNeighborUp.hasTag(tag) &&
        blockNeighborDown.hasTag(tag) &&
        blockNeighborEast.hasTag(tag) &&
        blockNeighborWest.hasTag(tag) &&
        blockNeighborNorth.hasTag(tag) &&
        blockNeighborSouth.hasTag(tag)
    );
};

/**
 * Verifica si todos los vecinos son del tipo determinado
 * @param {Block} block
 * @param {string} id 
 * @returns {boolean}
 */
export const checkAllNeighborHasBlockType = (block, id) => {
    const {
        blockNeighborDown, blockNeighborUp,
        blockNeighborEast, blockNeighborWest,
        blockNeighborNorth, blockNeighborSouth
    } = blockNeighbors(block);

    if(!blockNeighborUp || !blockNeighborDown ||
        !blockNeighborEast || !blockNeighborWest ||
        !blockNeighborNorth || !blockNeighborSouth) return;

    return (
        blockNeighborUp.typeId === id &&
        blockNeighborDown.typeId === id &&
        blockNeighborEast.typeId === id &&
        blockNeighborWest.typeId === id &&
        blockNeighborNorth.typeId === id &&
        blockNeighborSouth.typeId === id
    );
};


/**
 * Verifica si algún vecino tiene la etiqueta especificada
 * @param {Block} block
 * @param {string} tag 
 * @returns {boolean}
 */
export const checkTopOrBottomNeighborHasTag = (block, tag) => {
    const {
        blockNeighborDown, blockNeighborUp,
    } = blockNeighbors(block);

    if(!blockNeighborUp || !blockNeighborDown) return;

    return (
        blockNeighborUp.hasTag(tag) ||
        blockNeighborDown.hasTag(tag) 
    );
};


/**
 * Verifica si algún vecino es del tipo determinado
 * @param {Block} block
 * @param {string} id 
 * @returns {boolean}
 */
export const checkTopOrBottomNeighborHasBlockType = (block, id) => {
    const {
        blockNeighborDown, blockNeighborUp,
    } = blockNeighbors(block);

    if(!blockNeighborUp || !blockNeighborDown) return;

    return (
        blockNeighborUp.typeId === id ||
        blockNeighborDown.typeId === id 
    );
};



/**
 * Establece una propiedad del bloque
 * @param {Block} block 
 * @param {string} property 
 * @param {any} value 
 */
export const setBlockProperty = (block, property, value) => {
    let permutation = block.permutation;
    if(block.typeId === 'minecraft:air') return
    permutation = permutation.withState(property, value);

    if(block.typeId === 'minecraft:air') return
    block.setPermutation(permutation);

    return block;
};



/**
 * Rompe el bloque actual en la dimension correspondiente
 * @param {Block} block 
 * @param {Dimension} dimension 
 */
export const setBlockToAir = (block, dimension) => {
    dimension.runCommand(`setblock ${block.x} ${block.y} ${block.z} air destroy`)
}


/**
 * Función recursiva para verificar si algún vecino (o vecinos de vecinos) con un tag específico tiene las coordenadas dadas
 * @param {Block} startBlock 
 * @param {string} tag 
 * @param {number} x 
 * @param {number} y 
 * @param {number} z 
 * @returns 
 */
export const findNeighborWithTagAndCoordinates = (startBlock, tag, x, y, z) => {
    if (!startBlock) return { found: false, message: "No starting block provided." };

    const queue = [startBlock];
    const visited = new Set();
    let tagFound = false;

    while (queue.length > 0) {
        const block = queue.shift();
        
        const { x: bx, y: by, z: bz } = { x: block.x, y: block.y, z: block.z };
        const blockId = `${bx},${by},${bz}`;

        if (!visited.has(blockId)) {
            visited.add(blockId);

            if (block.hasTag(tag)) {
                tagFound = true;
                if (bx === x && by === y && bz === z) {
                    return { found: true, block };
                }
            }

            const neighbors = blockNeighbors(block);
            for (let direction in neighbors) {
                const neighbor = neighbors[direction];
                if (neighbor && neighbor.hasTag(tag) && !visited.has(`${neighbor.x},${neighbor.y},${neighbor.z}`)) {
                    queue.push(neighbor);
                }
            }
        }
    }

    if (tagFound) {
        return { found: false, message: "No neighbor with the given tag matches the coordinates." };
    } else {
        return { found: false, message: "No neighbor has the given tag." };
    }
}