import * as mc from "@minecraft/server";
import * as mcui from "@minecraft/server-ui";
import { icons } from "./storageIcons";
import * as racoAPI from "../raco-API";
import { hasRedstonePower } from "../hasRedstonePower";

const invertFace = {north: 'south', south: 'north', east: 'west', west: 'east', up: 'down', down: 'up', above: 'below', below: 'above'}

mc.world.beforeEvents.worldInitialize.subscribe(xZ => {
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:output_plate', {
        onTick: event => {
            const { dimension, block } = event;
            if (hasRedstonePower(block, ['north', 'south', 'west', 'east', 'below'], true) && !block.permutation.getState('vtng_rt:on')) block.dimension.spawnParticle('vtng_rt:redstone_out', block.center()), block.setPermutation(block.permutation.withState('vtng_rt:on', true));
            else if (block.getRedstonePower() <= 0 && block.permutation.getState('vtng_rt:on')) block.dimension.spawnParticle('vtng_rt:redstone_out', block.center()), block.setPermutation(block.permutation.withState('vtng_rt:on', false));
            if (block.permutation.getState('vtng_rt:on')) {
				let blockRotation = block.permutation.getState('minecraft:block_face');
				if (blockRotation == 'up') blockRotation = 'above'
				if (blockRotation == 'down') blockRotation = 'below'
				const frontBlock = block[blockRotation]();
				const backBlock = block[invertFace[blockRotation]]();
				if (!backBlock?.getComponent('inventory')) return;

				//if (frontBlock.typeId != 'vtng_rt:storage_duct') {
				//	const container = backBlock?.getComponent('inventory').container;
				//	for (let slot = 0; slot < container.size; slot++) {
				//		let item = container.getItem(slot);
				//		if (item) {
				//			clearItem(container, slot, 1)
				//			item.amount = 1;
				//			let spawnLoc
				//			if (blockRotation == 'north') spawnLoc = {x: frontBlock.center().x, y: frontBlock.center().y - 0.3, z: frontBlock.center().z + 0.35};
				//			if (blockRotation == 'south') spawnLoc = {x: frontBlock.center().x, y: frontBlock.center().y - 0.3, z: frontBlock.center().z - 0.35};
				//			if (blockRotation == 'east') spawnLoc = {x: frontBlock.center().x - 0.35, y: frontBlock.center().y - 0.3, z: frontBlock.center().z};
				//			if (blockRotation == 'west') spawnLoc = {x: frontBlock.center().x + 0.35, y: frontBlock.center().y - 0.3, z: frontBlock.center().z};
				//			if (blockRotation == 'above') spawnLoc = {x: frontBlock.center().x, y: frontBlock.center().y - 0.7, z: frontBlock.center().z}
				//			if (blockRotation == 'below') spawnLoc = {x: frontBlock.center().x, y: frontBlock.center().y + 0.7, z: frontBlock.center().z}
				//			dimension.spawnItem(item, spawnLoc);
				//			dimension.playSound('random.pop', block.location, { volume: 0.7 });
				//			return;
				//		}
				//	};
				//};

				const pendingBlocks = [block];
				let visitedBlocks = [`${block.x},${block.y},${block.z}`, `${backBlock.x},${backBlock.y},${backBlock.z}`];
				const containers = [];

				while (pendingBlocks.length > 0) {
					const block = pendingBlocks.shift();
					const faces = [block?.above(), block?.below(), block?.north(), block?.south(), block?.west(), block?.east()];
					for (const face of faces) {
						if (face?.typeId == 'vtng_rt:storage_duct' && !visitedBlocks?.includes(`${face.x},${face.y},${face.z}`)) {
							visitedBlocks?.push(`${face.x},${face.y},${face.z}`);
							pendingBlocks.push(face);
						};
						if (face?.getComponent('inventory')?.container && !visitedBlocks?.includes(`${face.x},${face.y},${face.z}`)) {
							visitedBlocks?.push(`${face.x},${face.y},${face.z}`);
							containers?.push(face);
						}
					}
				};

				const oldContainer = backBlock?.getComponent('inventory').container;
				for (let slot = 0; slot < oldContainer.size; slot++) {
					const item = oldContainer.getItem(slot);
					if (item) {
                        dimension.playSound('random.pop2', block.center());
						containerToContainer(backBlock, item?.typeId, 1, containers);
						return;
					};
				};
			};
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:central', {
        onPlayerInteract: event => {
            acessCentral(event.player, event.block, true)
        }
    })
});

mc.world.afterEvents.playerPlaceBlock.subscribe((data)=>{
    const player = data.player
    const block = data. block
    if (block.typeId == 'vtng_rt:storage_duct' || block?.getComponent("inventory")?.container) {
        setSides(block, false)
    }
    if (block?.typeId == 'vtng_rt:storage_central'){
        let entity = block.dimension.spawnEntity('vtng_rt:storage_central', block.center())
    }
});

mc.world.beforeEvents.playerBreakBlock.subscribe((data)=>{
    const player = data.player
    const block = data.block
    if (block.typeId == 'vtng_rt:storage_duct' || block?.getComponent("inventory")?.container) {
        mc.system.run(()=>{setSides(block, false)})
    }
    if (block?.typeId == 'vtng_rt:storage_central'){
        mc.system.run(()=>{
            let entity = block.dimension.getEntitiesAtBlockLocation(block.center())
            for (const entitye of entity){
                if (entitye?.typeId == 'vtng_rt:storage_central'){
                    entitye?.remove()
                }
            }
        })
    }
});

mc.world.beforeEvents.itemUseOn.subscribe((data)=>{
    const player = data.source
    const item = data.itemStack
    const block = data.block
    if (item?.typeId == 'vtng_rt:storage_remote'){
        if (block?.typeId == 'vtng_rt:storage_link'){
            mc.system.run(()=>{
                if (!player.hasTag('clicked')){
                    player.addTag('clicked')
                    mc.system.runTimeout(()=>{player.removeTag('clicked')},3)
                    item.setLore([`${block.dimension.id},${block.x},${block.y},${block.z}`])
                    player.getComponent("equippable")?.setEquipment("Mainhand", item)
                    player.sendMessage({translate: 'vtng_rt.storage_link_connect.warn'});
                    player.playSound('note.bell');
                }
            })
        }
    }
});

mc.world.afterEvents.itemUse.subscribe((data)=>{
    const player = data.source
    const item = data.itemStack
    if (item?.typeId == 'vtng_rt:storage_remote'){
        if (item.getLore()) {
            const split = item?.getLore()[0]?.split(',')
            if (split){
                player.runCommand(`tickingarea add circle ${Number(split[1])} ${Number(split[2])} ${Number(split[3])} 2 ${item?.getLore()[0].replace(/,/g, '')}`);
                mc.system.runTimeout(() => {
                    const block = mc.world.getDimension(split[0])?.getBlock({x:Number(split[1]),y:Number(split[2]),z:Number(split[3])});
                    if (block?.typeId == 'vtng_rt:storage_link'){
                        if (block?.below()?.typeId == 'vtng_rt:storage_central'){
                            acessCentral(player, block.below())
                        }
                    }
                }, 3);
            }
        }
    }
})

function setSides(block, once){
    const faces = [
        "above",
        "below",
        "north",
        "south",
        "west",
        "east"
    ]
    const pipeConnects = [
        "vtng_rt:output_plate",
        "vtng_rt:storage_duct",
        "vtng_rt:storage_central"
    ]
    for (const face of faces){
        if (pipeConnects.includes(block[face]()?.typeId) || (block[face]()?.getComponent('inventory')?.container && block[face]()?.typeId !='minecraft:hopper')){
            if (block?.typeId == 'vtng_rt:storage_duct') block.setPermutation(block.permutation.withState(`vtng_rt:${face}`, 'cable'))
            if (!once){
                setSides(block[face](), true)
            }
        } else 
        if (block?.typeId == 'vtng_rt:storage_duct')  block.setPermutation(block.permutation.withState(`vtng_rt:${face}`, 'none'))
    }
}

function acessCentral(player, block, remote){
    let visitedBlocks = []
    let visitedContainers = []
    let allItems = {}
    cableRecursion(block)
    function cableRecursion(block){
        if (!visitedBlocks?.includes(`${block.dimension.id},${block.x},${block.y},${block.z}`)){
            visitedBlocks?.push(`${block.dimension.id},${block.x},${block.y},${block.z}`)
            const faces = [
                block?.above(),
                block?.below(),
                block?.north(),
                block?.south(),
                block?.west(),
                block?.east()
            ]
            for (const face of faces){
                if (face?.typeId == 'vtng_rt:storage_duct'){
                    cableRecursion(face)
                } else
                if (face?.getComponent('inventory')?.container && !visitedContainers?.includes(`${block.dimension.id},${block.x},${block.y},${block.z}`) && face?.typeId != 'minecraft:hopper'){
                    visitedContainers?.push(`${face.dimension.id},${face.x},${face.y},${face.z}`)
                }
            }
        }
    }
    const firstMenu = new mcui.ActionFormData()
    const item = player.getComponent("equippable")?.getEquipment("Mainhand")
    firstMenu.title("Storage Central")
    firstMenu.body("Select:")
    firstMenu.button("Import Item", 'textures/ui/icon_import')
    firstMenu.button("Export Item", 'textures/vtng_rt/common/ui/export')
    if (remote) firstMenu.button(`Quickly Export\n${item ? transformKey(player.getComponent("equippable")?.getEquipment("Mainhand")?.typeId) : 'None'}`, player.getComponent("equippable")?.getEquipment("Mainhand") ? icons[player.getComponent("equippable")?.getEquipment("Mainhand")?.typeId] : 'textures/ui/refresh_hover')
    if (remote) firstMenu.button("Hopper Filter", 'textures/items/hopper')
    firstMenu.show(player).then((form)=>{
        if (!form.canceled){
            if (form.selection == 0){
                for (const containerLoc of visitedContainers){
                    const split = containerLoc.split(',')
                    const containerBlock = mc.world.getDimension(split[0])?.getBlock({x:Number(split[1]), y:Number(split[2]), z:Number(split[3])})
                    const container = containerBlock?.getComponent('inventory')?.container
                    if (container){
                        for (let i = 0; i < container?.size; i++){
                            const item = container.getItem(i)
                            if (item){
                                const foundItem = {}
                                foundItem[item?.typeId] = item?.amount
                                combineItems(allItems, foundItem)
                            }
                        }
                    }
                }
                const importMenu = new mcui.ActionFormData()
                importMenu.title("Storage Central - Import")
                importMenu.body("Select:")
                importMenu.button("Search Item By Id", icons['search'])
                let sortedEntries = Object.entries(allItems).sort(([, a], [, b]) => b - a)
                for (let i = 0; i < sortedEntries.length; i++) {
                    const [key, value] = sortedEntries[i]
                    importMenu.button(`${transformKey(key)}\nx${value}`, icons[key] ? icons[key] : icons['without_image'])
                }
                importMenu.show(player).then((form)=>{
                    if (!form.canceled){
                        if (form.selection == 0){
                            search()
                            function search(){
                                const searchForm = new mcui.ModalFormData()
                                searchForm.title('Search Item By Id')
                                searchForm.textField('Item Id:', 'Ex.:grass')
                                searchForm.show(player).then((form)=>{
                                    if (!form.canceled){
                                        const filteredItems = filterProperties(allItems, form.formValues[0])
                                        const searchedMenu = new mcui.ActionFormData()
                                        searchedMenu.title(`Results for "${form.formValues[0]}"`)
                                        searchedMenu.button("Search Item By Id", icons['search'])
                                        searchedMenu.body("Select:")
                                        let sortedEntries = Object.entries(filteredItems).sort(([, a], [, b]) => b - a)
                                        for (let i = 0; i < sortedEntries.length; i++) {
                                            const [key, value] = sortedEntries[i]
                                            searchedMenu.button(`${transformKey(key)}\nx${value}`, icons[key])
                                        }
                                        searchedMenu.show(player).then((form)=>{
                                            if (!form.canceled){
                                                if (form.selection == 0){
                                                    search()
                                                } else {
                                                    const selection = form.selection-1
                                                    itemAmoutForm(player, selectedItemId(selection, sortedEntries), visitedContainers, 'import')
                                                }
                                            } else player.runCommand(`tickingarea remove ${block.x} ${block.y} ${block.z}`);
                                        })
                                    } else player.runCommand(`tickingarea remove ${block.x} ${block.y} ${block.z}`);
                                })
                            }
                        } else {
                            const selection = form.selection-1
                            itemAmoutForm(player, selectedItemId(selection, sortedEntries), visitedContainers, 'import')
                        }
                    } else player.runCommand(`tickingarea remove ${block.x} ${block.y} ${block.z}`);
                })
            } else
            if (form.selection == 1){
                const container = player?.getComponent('inventory')?.container
                if (container){
                    for (let i = 0; i < container?.size; i++){
                        const item = container.getItem(i)
                        if (item){
                            const foundItem = {}
                            foundItem[item?.typeId] = item?.amount
                            combineItems(allItems, foundItem)
                        }
                    }
                }
                if (Object.keys(allItems)?.length){
                    const exportMenu = new mcui.ActionFormData()
                    exportMenu.title("Storage Central - Export")
                    exportMenu.body("Select:")
                    let sortedEntries = Object.entries(allItems).sort(([, a], [, b]) => b - a)
                    for (let i = 0; i < sortedEntries.length; i++) {
                        const [key, value] = sortedEntries[i]
                        exportMenu.button(`${transformKey(key)}\nx${value}`, icons[key] ? icons[key] : icons['without_image'])
                    }
                    exportMenu.show(player).then((form)=>{
                        if (!form.canceled){
                            const selection = form.selection
                            itemAmoutForm(player, selectedItemId(selection, sortedEntries), visitedContainers, 'export')
                        } else player.runCommand(`tickingarea remove ${block.x} ${block.y} ${block.z}`);
                    })
                }
            } else
            if (form.selection == 2){
                const item = player.getComponent("equippable")?.getEquipment("Mainhand")
                if (item) itemAmoutForm(player, {key: item?.typeId, value: item?.amount}, visitedContainers, 'export'); else acessCentral(player, block, remote)
            } else
            if (form.selection == 3){
                filterFormOpen()
                function filterFormOpen(){
                    const filterForm = new mcui.ActionFormData()
                    filterForm.title("Hopper Filter")
                    filterForm.body("Remove or Add a filter:")
                    filterForm.button(`Add filter by Mainhand Item\n${transformKey(player.getComponent("equippable")?.getEquipment('Mainhand')?.typeId)}`, icons[player.getComponent("equippable")?.getEquipment('Mainhand')?.typeId])
                    let entities = block.dimension.getEntitiesAtBlockLocation(block.center())
                    for (const entity of entities){
                        if (entity?.typeId == 'vtng_rt:storage_central'){
                            let property = entity?.getDynamicProperty('vtng_rt:central_filters')
                            if (property){
                                for (const itemId of JSON.parse(property)){
                                    filterForm.button(transformKey(itemId), icons[itemId] ? icons[itemId] : icons['without_image'])
                                }
                            }
                            filterForm.show(player).then((form)=>{
                                if (!form.canceled){
                                    if (form.selection == 0){
                                        if (player.getComponent("equippable")?.getEquipment('Mainhand')){
                                            addStringToDynamicPropertyArray(entity, 'vtng_rt:central_filters', player.getComponent("equippable")?.getEquipment('Mainhand')?.typeId)
                                            filterFormOpen()
                                        }
                                    } else {
                                        removeStringToDynamicPropertyArray(entity, 'vtng_rt:central_filters', JSON.parse(property)[form.selection-1])
                                        filterFormOpen()
                                    }
                                } else player.runCommand(`tickingarea remove ${block.x} ${block.y} ${block.z}`);
                            })
                        }
                        break
                    }
                }
            }
        } else player.runCommand(`tickingarea remove ${block.x} ${block.y} ${block.z}`);
    })
}

function containerToContainer(fromBlock, itemId, amount, containersList){
    const fromContainer = fromBlock?.getComponent("inventory")?.container
    for (let i = 0; i < fromContainer.size; i++){
        const item = fromContainer?.getItem(i)
        if (item?.typeId == itemId ) {
            if (item.amount >= amount){
                sendItemToContainer();
            } else if (item.amount < amount){ 
                sendItemToContainer()
                containerToContainer(fromBlock, itemId, amount-item.amount, containersList)
            };
            function sendItemToContainer() {
                for (const containerBlock of containersList) {
                    const container = containerBlock?.getComponent('inventory')?.container
                    if (container) {
                        let hasSpace = false
                        if (container.emptySlotsCount > 0) hasSpace = true; else
                        for (let o = 0; o < container.size; o++){
                            const containerItem = container.getItem(o)
                            if (containerItem?.isStackableWith(item) && containerItem.amount + amount <= item.maxAmount){
                                hasSpace = true
                            };
                        };
                        if (hasSpace){
                            if (item.amount >= amount){
                                racoAPI.clearItem(fromContainer, i, amount)
                                item.amount = amount
                                container.addItem(item)
								return true;
                                } else 
                            if (item.amount < amount){ 
                                fromContainer.setItem(i,)
                                container.addItem(item)
								return true;
                                };
                            break
                        };					
                    };
                };
            };
            break
        };
    };
};

function itemAmoutForm(player, selected, visitedContainers, from){
    const itemId = selected.key
    const maxAmount = selected.value
    const amountForm = new mcui.ModalFormData()
    amountForm.title(`${transformKey(itemId)} Amount`)
    amountForm.slider("Select Amount", 1, maxAmount, 1, 1)
    amountForm.show(player).then((form)=>{
        if (!form.canceled){
            const selection = form.formValues[0]
            if (from == 'import'){
                containersToPlayer(player, itemId, selection, visitedContainers, true)
            }
            if (from == 'export'){
                playerToContainers(player, itemId, selection, visitedContainers)
            }
        }
    })
}

function selectedItemId(selection, object){
    const [key, value] = object[selection]
    return {key: key, value: value}
}

function combineItems(list, value) {
    for (const key in value) {
        list[key] = (list[key] || 0) + value[key]
    }
}
const transformKey = str => 
    str.split(':')[1].replace(/_/g, ' ').toLowerCase().replace(/\b\w/g, c => c.toUpperCase())

const normalize = str => str.toLowerCase().replace(/[_ :]/g, '')
const filterProperties = (obj, search) => Object.fromEntries(
    Object.entries(obj).filter(([k]) => normalize(k).includes(normalize(search)))
)

function playerToContainers(player, itemId, amount, containersList){
    const playerContainer = player?.getComponent("inventory")?.container
    for (let i = 0; i < playerContainer.size; i++){
        const playerItem = playerContainer?.getItem(i)
        if (playerItem?.typeId == itemId){
            if (playerItem.amount >= amount){
                sendItemToContainer()
            } else if (playerItem.amount < amount){ 
                sendItemToContainer()
                playerToContainers(player, itemId, amount-playerItem.amount, containersList)
            }
            function sendItemToContainer(){
                for (const containerLoc of containersList){
                    const split = containerLoc.split(',')
                    const containerBlock = mc.world.getDimension(split[0])?.getBlock({x:Number(split[1]), y:Number(split[2]), z:Number(split[3])})
                    const container = containerBlock?.getComponent('inventory')?.container
                    if (container){
                        let hasSpace = false
                        if (container.emptySlotsCount > 0) hasSpace = true; else
                        for (let o = 0; o < container.size; o++){
                            const containerItem = container.getItem(o)
                            if (containerItem?.isStackableWith(playerItem) && containerItem.amount + playerItem.amount <= playerItem.maxAmount){
                                hasSpace = true
                            }
                        }
                        if (hasSpace){
                            if (playerItem.amount >= amount){
                                racoAPI.clearItem(playerContainer, i, amount)
                                playerItem.amount = amount
                                container.addItem(playerItem)
                                } else 
                            if (playerItem.amount < amount){ 
                                playerContainer.setItem(i,)
                                container.addItem(playerItem)
                                }
                            break
                        }
                    }
                }
            }
            break
        }
    }
}
function containersToPlayer(player, itemId, amount, containersList, isPlayer){
    for (const containerLoc of containersList){
        const split = containerLoc.split(',')
        const containerBlock = mc.world.getDimension(split[0])?.getBlock({x:Number(split[1]), y:Number(split[2]), z:Number(split[3])})
        const container = containerBlock?.getComponent('inventory')?.container
        if (container){
            for (let o = 0; o < container.size; o++){
                const containerItem = container.getItem(o)
                if (containerItem?.typeId == itemId){
                    if (containerItem.amount >= amount){
                        sendItemToPlayer()
                    } else if (containerItem.amount < amount){ 
                        sendItemToPlayer()
                        containersToPlayer(player, itemId, amount-containerItem.amount, containersList, isPlayer)
                    }
                    function sendItemToPlayer(){
                        const playerContainer = player?.getComponent("inventory")?.container
                        if (playerContainer){
                            let hasSpace = false
                            if (playerContainer?.emptySlotsCount > 0) hasSpace = true; else
                            for (let i = 0; i < playerContainer.size; i++){
                                const playerItem = playerContainer?.getItem(i)
                                if (playerItem?.isStackableWith(containerItem) && playerItem.amount + containerItem.amount <= containerItem.maxAmount){
                                    hasSpace = true
                                }
                            }
                            if (hasSpace){
                                if (containerItem.amount >= amount){
                                    racoAPI.clearItem(container, o, amount)
                                    containerItem.amount = amount
                                    playerContainer.addItem(containerItem)
                                } else 
                                if (containerItem.amount < amount){ 
                                    container.setItem(o,)
                                    playerContainer.addItem(containerItem)
                                }
                            } else if (isPlayer){
                                if (containerItem.amount >= amount){
                                    racoAPI.clearItem(container, o, amount)
                                    containerItem.amount = amount
                                    player.dimension.spawnItem(containerItem, player.location)
                                } else 
                                if (containerItem.amount < amount){ 
                                    container.setItem(o,)
                                    player.dimension.spawnItem(containerItem, player.location)
                                }
                            }
                        }
                    }
                    break
                } 
            }
        }
    }
}