package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Chest;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;

import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils;
import io.github.thebusybiscuit.cscorelib2.materials.MaterialCollections;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.cscorelib2.scheduling.TaskQueue;
import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SimpleSlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;

public class Composter extends SimpleSlimefunItem<BlockUseHandler> implements RecipeDisplayItem {

    private static final BlockFace[] outputFaces = { BlockFace.UP, BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST };
    private final List<ItemStack> recipes;

    public Composter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
        super(category, item, recipeType, recipe);

        recipes = getMachineRecipes();
    }

    @Override
    public List<ItemStack> getDisplayRecipes() {
        return recipes;
    }

    private List<ItemStack> getMachineRecipes() {
        List<ItemStack> items = new LinkedList<>();

        for (Material leave : MaterialCollections.getAllLeaves()) {
            items.add(new ItemStack(leave, 8));
            items.add(new ItemStack(Material.DIRT));
        }

        for (Material sapling : MaterialCollections.getAllSaplings()) {
            items.add(new ItemStack(sapling, 8));
            items.add(new ItemStack(Material.DIRT));
        }

        items.add(new ItemStack(Material.STONE, 4));
        items.add(new ItemStack(Material.NETHERRACK));

        items.add(new ItemStack(Material.SAND, 2));
        items.add(new ItemStack(Material.SOUL_SAND));

        items.add(new ItemStack(Material.WHEAT, 4));
        items.add(new ItemStack(Material.NETHER_WART));

        return items;
    }

    @Override
    public BlockUseHandler getItemHandler() {
        return e -> {
            Optional<Block> block = e.getClickedBlock();

            if (block.isPresent()) {
                e.cancel();

                Player p = e.getPlayer();
                Block b = block.get();

                if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES)) {
                    ItemStack input = e.getItem();
                    ItemStack output = getOutput(p, input);

                    if (output != null) {
                        TaskQueue tasks = new TaskQueue();

                        tasks.thenRepeatEvery(30, 10, () -> {
                            Material material = input.getType().isBlock() ? input.getType() : Material.HAY_BLOCK;
                            b.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, material);
                        });

                        tasks.thenRun(20, () -> {
                            p.getWorld().playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F);
                            pushItem(b, output.clone());
                        });

                        tasks.execute(SlimefunPlugin.instance);
                    }
                    else {
                        SlimefunPlugin.getLocalization().sendMessage(p, "machines.wrong-item", true);
                    }
                }
            }
        };
    }

    private void pushItem(Block b, ItemStack output) {
        Optional<Inventory> outputChest = findOutputChest(b, output);

        if (outputChest.isPresent()) {
            outputChest.get().addItem(output);
        }
        else {
            Location loc = b.getRelative(BlockFace.UP).getLocation();
            b.getWorld().dropItemNaturally(loc, output);
        }
    }

    private Optional<Inventory> findOutputChest(Block b, ItemStack output) {
        for (BlockFace face : outputFaces) {
            Block potentialOutput = b.getRelative(face);

            if (potentialOutput.getType() == Material.CHEST) {
                String id = BlockStorage.checkID(potentialOutput);

                if (id != null && id.equals("OUTPUT_CHEST")) {
                    // Found the output chest! Now, let's check if we can fit the product in it.
                    Inventory inv = ((Chest) potentialOutput.getState()).getInventory();

                    if (InvUtils.fits(inv, output)) {
                        return Optional.of(inv);
                    }
                }
            }
        }

        return Optional.empty();
    }

    private ItemStack getOutput(Player p, ItemStack input) {
        for (int i = 0; i < recipes.size(); i += 2) {
            ItemStack convert = recipes.get(i);

            if (convert != null && SlimefunUtils.isItemSimilar(input, convert, true)) {
                ItemStack removing = input.clone();
                removing.setAmount(convert.getAmount());
                p.getInventory().removeItem(removing);

                return recipes.get(i + 1);
            }
        }

        return null;
    }

}