import { world, system, BlockPermutation, ItemStack, EnchantmentType } from "@minecraft/server"
import { Vector } from './vector.js'



class PortableDrill {
    constructor() {
        this.ENCHANTMENT_TYPES = [
            { type: "efficiency", level: 1 },
            { type: "efficiency", level: 2 },
            { type: "efficiency", level: 3 },
            { type: "efficiency", level: 4 },
            { type: "efficiency", level: 5 },
            { type: "fortune", level: 1 },
            { type: "fortune", level: 2 },
            { type: "fortune", level: 3 },
            { type: "silk_touch", level: 1 }
        ];
        this.BANNED_BLOCK = [
            "minecraft:bedrock",
            "minecraft:light_block",
            "minecraft:reinforced_deepslate",
            "minecraft:barrier",
            "minecraft:end_portal_frame",
            "minecraft:structure_block",
            "minecraft:structure_void",
            "minecraft:command_block",
            "minecraft:chain_command_block",
            "minecraft:repeating_command_block",
            "minecraft:water",
            "minecraft:flowing_water",
            "minecraft:lava",
            "minecraft:flowing_lava",
        ]

        this.MINECRAFT_ORES = [
            "minecraft:coal_ore",
            "minecraft:deepslate_coal_ore",
            "minecraft:iron_ore",
            "minecraft:deepslate_iron_ore",
            "minecraft:copper_ore",
            "minecraft:deepslate_copper_ore",
            "minecraft:gold_ore",
            "minecraft:deepslate_gold_ore",
            "minecraft:redstone_ore",
            "minecraft:lit_redstone_ore",
            "minecraft:deepslate_redstone_ore",
            "minecraft:lit_deepslate_redstone_ore",
            "minecraft:emerald_ore",
            "minecraft:deepslate_emerald_ore",
            "minecraft:lapis_ore",
            "minecraft:deepslate_lapis_ore",
            "minecraft:diamond_ore",
            "minecraft:deepslate_diamond_ore",
            "minecraft:nether_gold_ore",
            "minecraft:quartz_ore",
            "minecraft:ancient_debris",
            "minecraft:stone",
            "minecraft:deepslate",
        ];

        this.MINECRAFT_ORES_LOOT = [
            "minecraft:coal",
            "minecraft:coal",
            "minecraft:raw_iron",
            "minecraft:raw_iron",
            "minecraft:raw_copper",
            "minecraft:raw_copper",
            "minecraft:raw_gold",
            "minecraft:raw_gold",
            "minecraft:redstone",
            "minecraft:redstone",
            "minecraft:redstone",
            "minecraft:redstone",
            "minecraft:emerald",
            "minecraft:emerald",
            "minecraft:lapis_lazuli",
            "minecraft:lapis_lazuli",
            "minecraft:diamond",
            "minecraft:diamond",
            "minecraft:gold_nugget",
            "minecraft:quartz",
            "minecraft:ancient_debris",
            "minecraft:stone",
            "minecraft:deepslate",
        ];

        this.MINECRAFT_STONE_TYPES = [
            "minecraft:stone",
            "minecraft:cobblestone",
            "minecraft:granite",
            "minecraft:diorite",
            "minecraft:andesite",
            "minecraft:tuff",
            "minecraft:deepslate",
            "minecraft:cobbled_deepslate",
            "minecraft:dripstone_block",
            "minecraft:calcite",
            "minecraft:basalt",
            "minecraft:smooth_basalt",
            "minecraft:blackstone",
            "minecraft:gilded_blackstone"
        ];

        this.tickCpt = 0
        this.inMining = {}

    }

    onItemUse(e) {
        const { itemStack, source } = e;
        if (source.typeId === "minecraft:player" && (itemStack.typeId === "pb_red:spawn_portable_small_drill" || itemStack.typeId === "pb_red:spawn_portable_big_drill")) {
            const minecart = source.getEntitiesFromViewDirection()[0]?.entity;
            if (!minecart) return;
            const alreadydrill = source.dimension.getEntities({ type: "pb_red:portable_drill", location: minecart.location, maxDistance: 1 })[0]
            if (alreadydrill) return;
            if (minecart.typeId === "minecraft:minecart" && this.isWithinDistance(minecart.location, source.location, 6)) {
                minecart.runCommand(`ride @s summon_rider pb_red:portable_drill`);
                const drillEntity = source.dimension.getEntities({ type: "pb_red:portable_drill", location: minecart.location, maxDistance: 1 })[0]
                drillEntity.nameTag = "§r"
                drillEntity.setRotation(minecart.getRotation())
                drillEntity.setProperty("pb_red:big_drill", itemStack.typeId.includes("big"))
                minecart.dimension.runCommand(`playsound mob.agent.spawn @a ${minecart.location.x} ${minecart.location.y} ${minecart.location.z}`);
                source.runCommand(`clear @s ${itemStack.typeId} 0 1`);
            }
        }
    }

    isWithinDistance(pos1, pos2, maxDistance) {
        const distance = Math.sqrt(
            Math.pow(pos2.x - pos1.x, 2) +
            Math.pow(pos2.y - pos1.y, 2) +
            Math.pow(pos2.z - pos1.z, 2)
        );
        return distance <= maxDistance;
    }

    onScriptEventReceive(data) {
        const { id, sourceEntity } = data;

        if (id === 'pb_red:portable_drill_despawn') {
            this.handledrillDespawn(sourceEntity);
        } else if (id === 'pb_red:portable_drill_working') {
            this.handledrill(sourceEntity);
        } else if (id === 'pb_red:portable_drill_interact') {
            const player = sourceEntity.dimension.getEntities({ type: "minecraft:player", location: sourceEntity.location, maxDistance: 10 })[0]
            if (!player) return
            let selectedSlot = player.selectedSlotIndex;
            let handItem = player.getComponent("inventory").container.getItem(selectedSlot);
            if (!handItem) return

            if (handItem.typeId == "pb_red:redstone_wrench") {
                if (player.isSneaking) {
                    const efficiency = sourceEntity.getProperty('pb_red:efficiency')
                    const fortune = sourceEntity.getProperty('pb_red:fortune')
                    const silkTouch = sourceEntity.getProperty('pb_red:silk_touch')

                    sourceEntity.dimension.runCommand(`playsound random.click @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`)
                    player.sendMessage(`Portable Drill upgrades:${(efficiency == 0 && fortune == 0 && silkTouch == 0) ? "\n- §cNone§r" : ""}${(efficiency > 0) ? ("\n- §dEfficiency§5 " + efficiency + "§r") : ""}${(fortune > 0) ? ("\n- §dFortune§5 " + fortune + "§r") : ""}${(silkTouch > 0) ? ("\n- §dSilk Touch§r") : ""}`)
                    return;

                } else {

                    this.drillBonusRotation(sourceEntity);
                }

            } else if (handItem.typeId.includes('enchanted_book')) {
                const enchantment = handItem.getComponent("enchantable");

                for (let i = 0; i < this.ENCHANTMENT_TYPES.length; i++) {
                    let enchantmentType = this.ENCHANTMENT_TYPES[i];
                    if (enchantment.hasEnchantment(new EnchantmentType(enchantmentType.type))) {
                        const hasFortune = sourceEntity.getProperty('pb_red:fortune') !== 0
                        const hasSilkTouch = sourceEntity.getProperty('pb_red:silk_touch') !== 0
                        if (enchantmentType.type == "silk_touch" && hasFortune) {
                            player.runCommand(`tellraw @s {"rawtext":[{"text":"§cCan't upgrade Portable Drill with §4Silk Touch§c because it already have §4Fortune§c upgrade."}]}`)
                            return;
                        }
                        if (enchantmentType.type == "fortune" && hasSilkTouch) {
                            player.runCommand(`tellraw @s {"rawtext":[{"text":"§cCan't upgrade Portable Drill with §4Fortune§c because it already have §4Silk Touch§c upgrade."}]}`)
                            return;
                        }
                        let enchantLevel = enchantment.getEnchantment(new EnchantmentType(enchantmentType.type)).level;
                        sourceEntity.setProperty(`pb_red:${enchantmentType.type}`, enchantLevel);
                        player.getComponent("inventory").container.setItem(selectedSlot, null);
                        player.runCommand(`tellraw @s {"rawtext":[{"text":"Upgraded Portable Drill with §d${enchantmentType.type == "silk_touch" ? "silk touch" : enchantmentType.type} ${enchantmentType.type == "silk_touch" ? "" : enchantLevel}."}]}`)
                        sourceEntity.dimension.runCommand(`playsound random.orb @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`)
                        this.tickCpt = 1
                        return;
                    }
                }
            }
        }
    }

    drillBonusRotation(sourceEntity) {
        let onRailPlacer = sourceEntity.getProperty("pb_red:is_on_rail_placer")
        if (onRailPlacer) return

        let bonusRotation = sourceEntity.getDynamicProperty("pb_red:portable_drill:bonus_rotation") || 0
        bonusRotation += 90
        if (bonusRotation >= 360) bonusRotation -= 360

        sourceEntity.dimension.runCommand(`playsound vr.stutterturn @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`);

        sourceEntity.setDynamicProperty("pb_red:portable_drill:bonus_rotation", bonusRotation)
    }

    handledrillDespawn(sourceEntity) {
        const inv = sourceEntity.getComponent('inventory').container;
        const location = { x: sourceEntity.location.x, y: sourceEntity.location.y + 1, z: sourceEntity.location.z };
        for (let i = 0; i < inv.size; i++) {
            const item = inv.getItem(i);
            if (item) sourceEntity.dimension.spawnItem(item, location);
        }
        const isBig = sourceEntity.getProperty("pb_red:big_drill")
        sourceEntity.dimension.spawnItem(new ItemStack(`pb_red:spawn_portable_${isBig ? "big" : "small"}_drill`, 1), location);

        const efficiency = sourceEntity.getProperty('pb_red:efficiency')
        const fortune = sourceEntity.getProperty('pb_red:fortune')
        const silkTouch = sourceEntity.getProperty('pb_red:silk_touch')

        if (efficiency > 0)
            sourceEntity.dimension.runCommand(`loot spawn ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z} loot "pb/redstone/upgrade/efficiency_${efficiency}"`)
        if (fortune > 0)
            sourceEntity.dimension.runCommand(`loot spawn ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z} loot "pb/redstone/upgrade/fortune_${fortune}"`)
        if (silkTouch > 0)
            sourceEntity.dimension.runCommand(`loot spawn ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z} loot "pb/redstone/upgrade/silk_touch"`)

        sourceEntity.dimension.runCommand(`playsound random.break @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`);
        sourceEntity.remove();
    }

    handledrill(sourceEntity) {
        if (!sourceEntity) return;

        try {
            let accessTest = sourceEntity.dimension;
        } catch {
            return;
        }

        let onRailPlacer = sourceEntity.getProperty("pb_red:is_on_rail_placer")

        if (!onRailPlacer) {
            const minecart = sourceEntity.dimension.getEntities({
                location: sourceEntity.location,
                maxDistance: 1,
                type: 'minecraft:minecart'
            })[0];

            if (!minecart) return;

            const bonusRotation = sourceEntity.getDynamicProperty("pb_red:portable_drill:bonus_rotation") || 0

            sourceEntity.setRotation({
                x: minecart.getRotation().x,
                y: minecart.getRotation().y + 90 + bonusRotation
            });
        }


        const inv = sourceEntity.getComponent('inventory').container;
        let globalSpeed = Math.sqrt(sourceEntity.getVelocity().x ** 2 + sourceEntity.getVelocity().z ** 2);

        const viewDirection = sourceEntity.getRotation().y;
        if (viewDirection % 90 !== 0) return;

        const offset = this.getOffsetsByViewDirection(viewDirection);
        if (!offset) return

        const blockPos = {
            x: sourceEntity.location.x + offset.x,
            y: sourceEntity.location.y,
            z: sourceEntity.location.z + offset.z
        };

        this.mineIfPossible(sourceEntity, inv, blockPos, globalSpeed, viewDirection);

        // Place all ores in the chest
        const itemsAround = sourceEntity.dimension.getEntities({
            location: sourceEntity.location,
            maxDistance: 6,
            type: "item"
        });

        itemsAround.forEach(itemEntity => {
            const itemStack = itemEntity.getComponent("minecraft:item").itemStack;
            if (!this.MINECRAFT_ORES_LOOT.includes(itemStack.typeId) && !this.MINECRAFT_ORES.includes(itemStack.typeId) || (["minecraft:stone", "minecraft:deepslate"].includes(itemStack.typeId))) return
            //console.warn(itemStack.typeId)
            let addedToInventory = false;
            for (let i = 0; i < inv.size; i++) {
                const invItem = inv.getItem(i);
                if (!invItem) {
                    inv.setItem(i, itemStack);
                    addedToInventory = true;
                    break;
                } else if (invItem.typeId === itemStack.typeId && itemStack.amount <= (invItem.maxAmount - invItem.amount)) {
                    invItem.amount += itemStack.amount;
                    inv.setItem(i, invItem);
                    addedToInventory = true;
                    break;
                }
            }

            if (addedToInventory) {
                itemEntity.remove();
            }
        });

    }

    getOffsetsByViewDirection(viewDirection) {
        switch (viewDirection) {
            case 0:
            case 360:
                return { x: 0, z: 1 };
            case 90:
            case -270:
                return { x: -1, z: 0 };
            case 180:
            case -180:
                return { x: 0, z: -1 };
            case 270:
            case -90:
                return { x: 1, z: 0 };
            default:
                return null;
        }
    }

    getBigDrillOffsetsByViewDirection(viewDirection) {
        switch (viewDirection) {
            case 0:
            case 360:
                return [
                    // Original
                    //    { x: 0, y: 0, z: 1 },
                    // Around
                    { x: -1, y: 1, z: 1 },
                    { x: 1, y: 1, z: 1 },
                    { x: -1, y: 0, z: 1 },
                    { x: 1, y: 0, z: 1 },
                    { x: -1, y: -1, z: 1 },
                    { x: 1, y: -1, z: 1 },
                    { x: 0, y: 1, z: 1 },
                    { x: 0, y: -1, z: 1 },
                ];
            case 90:
            case -270:
                return [
                    // Original
                    //   { x: -1, y: 0, z: 0 },
                    // Around
                    { x: -1, y: 1, z: -1 },
                    { x: -1, y: 1, z: 1 },
                    { x: -1, y: 0, z: -1 },
                    { x: -1, y: 0, z: 1 },
                    { x: -1, y: -1, z: -1 },
                    { x: -1, y: -1, z: 1 },
                    { x: -1, y: 1, z: 0 },
                    { x: -1, y: -1, z: 0 },
                ];
            case 180:
            case -180:
                return [
                    // Original
                    //   { x: 0, y: 0, z: -1 },
                    // Around
                    { x: -1, y: 1, z: -1 },
                    { x: 1, y: 1, z: -1 },
                    { x: -1, y: 0, z: -1 },
                    { x: 1, y: 0, z: -1 },
                    { x: -1, y: -1, z: -1 },
                    { x: 1, y: -1, z: -1 },
                    { x: 0, y: 1, z: -1 },
                    { x: 0, y: -1, z: -1 },
                ];
            case 270:
            case -90:
                return [
                    // Original
                    //   { x: 1, y: 0, z: 0 },
                    // Around
                    { x: 1, y: 1, z: -1 },
                    { x: 1, y: 1, z: 1 },
                    { x: 1, y: 0, z: -1 },
                    { x: 1, y: 0, z: 1 },
                    { x: 1, y: -1, z: -1 },
                    { x: 1, y: -1, z: 1 },
                    { x: 1, y: 1, z: 0 },
                    { x: 1, y: -1, z: 0 },
                ];
            default:
                return null;
        }
    }


    mineIfPossible(sourceEntity, inv, blockPos, globalSpeed, viewDirection) {
        const block = sourceEntity.dimension.getBlock(blockPos);
        if (!block) return

        const isBig = sourceEntity.getProperty("pb_red:big_drill")
        const efficiency = sourceEntity.getProperty('pb_red:efficiency')
        const fortune = sourceEntity.getProperty('pb_red:fortune')
        const silkTouch = sourceEntity.getProperty('pb_red:silk_touch')

        this.tickCpt++

        //  console.warn(block.typeId, blockPos.x, blockPos.y, blockPos.z)
        if (block.typeId == "minecraft:air" || (block.typeId.includes("minecraft:") && block.typeId.includes("rail"))) {
            if (globalSpeed > 0.01) {
                sourceEntity.setProperty("pb_red:drill_anim", true)

                if (this.tickCpt % 22 == 0 || this.tickCpt == 1) {
                    block.dimension.runCommand(`playsound pb_red.drill_${efficiency == 0 ? efficiency + 1 : efficiency} @a ${block.location.x} ${block.location.y} ${block.location.z} 0.2`)
                }
            } else {
                sourceEntity.setProperty("pb_red:drill_anim", false)
            }
            return;
        }
        if (this.BANNED_BLOCK.includes(block.typeId)) return

        if (this.tickCpt % 22 == 0 || this.tickCpt == 1) {
            block.dimension.runCommand(`playsound pb_red.drill_${efficiency == 0 ? efficiency + 1 : efficiency} @a ${block.location.x} ${block.location.y} ${block.location.z} 0.2`)
        }
        sourceEntity.setProperty("pb_red:drill_anim", true)


        if (this.inMining[sourceEntity.id] == true) return

        let miningDuration = Math.abs(efficiency - 5)
        let miningTime = 5 + (miningDuration * 20) + ((this.MINECRAFT_STONE_TYPES.includes(block.typeId) || this.MINECRAFT_ORES.includes(block.typeId)) ? 0 : 25)
        if (isBig) miningTime *= 3

        this.inMining[sourceEntity.id] = true

        const blocksToMine = [block]

        if (isBig) {
            const bigOffsets = this.getBigDrillOffsetsByViewDirection(viewDirection);
            for (const offset of bigOffsets) {
                const offsetPos = {
                    x: sourceEntity.location.x + offset.x,
                    y: sourceEntity.location.y + offset.y,
                    z: sourceEntity.location.z + offset.z
                };
                const offsetBlock = sourceEntity.dimension.getBlock(offsetPos);
                blocksToMine.push(offsetBlock)
            }
        }

        system.runTimeout(() => {
            for (const mineBlock of blocksToMine) {
                if (this.MINECRAFT_ORES.includes(mineBlock.typeId)) {
                    let oreId = silkTouch == 1 ? mineBlock.typeId : this.MINECRAFT_ORES_LOOT[this.MINECRAFT_ORES.indexOf(mineBlock.typeId)]
                    if (mineBlock.typeId == "minecraft:stone" && silkTouch == 0) oreId = "minecraft:cobblestone"
                    if (mineBlock.typeId == "minecraft:deepslate" && silkTouch == 0) oreId = "minecraft:cobbled_deepslate"

                    try {
                        if (Vector.distanceBetween(blockPos, sourceEntity.location) < 1.3) {
                            mineBlock.dimension.runCommand(`setblock ${mineBlock.location.x} ${mineBlock.location.y} ${mineBlock.location.z} air replace`)
                            mineBlock.dimension.runCommand(`playsound dig.stone @p ${mineBlock.location.x} ${mineBlock.location.y} ${mineBlock.location.z}`)
                            // console.warn(oreId)
                            sourceEntity.dimension.spawnItem(new ItemStack(oreId, (oreId == "minecraft:ancient_debris" || oreId == "minecraft:cobblestone" || oreId == "minecraft:cobbled_deepslate") ? 1 : fortune + 1), mineBlock.location)

                        }
                    } catch {
                        //console.warn("error Drill Loot")
                    }
                } else {
                    try {
                        // console.warn(Vector.distanceBetween(blockPos, sourceEntity.location))
                        if (Vector.distanceBetween(blockPos, sourceEntity.location) < 1.3 && !this.BANNED_BLOCK.includes(mineBlock.typeId)) {
                            mineBlock.dimension.runCommand(`setblock ${mineBlock.location.x} ${mineBlock.location.y} ${mineBlock.location.z} air destroy`)
                        }
                    } catch { }

                }
            }

            this.inMining[sourceEntity.id] = false
        }, miningTime)


    }

}




const drill = new PortableDrill();
world.afterEvents.itemUse.subscribe((e) => {
    drill.onItemUse(e)
})

system.afterEvents.scriptEventReceive.subscribe((data) => {
    drill.onScriptEventReceive(data)
})
