package org.allaymc.server.blockentity.component;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.allaymc.api.block.property.type.BlockPropertyType;
import org.allaymc.api.blockentity.BlockEntityInitInfo;
import org.allaymc.api.blockentity.component.BlockEntityBrewingStandBaseComponent;
import org.allaymc.api.blockentity.component.BlockEntityContainerHolderComponent;
import org.allaymc.api.blockentity.interfaces.BlockEntityBrewingStand;
import org.allaymc.api.eventbus.event.container.BrewingStandBrewEvent;
import org.allaymc.api.eventbus.event.container.BrewingStandConsumeFuelEvent;
import org.allaymc.api.eventbus.event.container.BrewingStandStartBrewEvent;
import org.allaymc.api.item.ItemStack;
import org.allaymc.api.item.interfaces.ItemAirStack;
import org.allaymc.api.item.recipe.PotionRecipe;
import org.allaymc.api.item.type.ItemTypes;
import org.allaymc.api.registry.Registries;
import org.allaymc.api.world.sound.SimpleSound;
import org.allaymc.server.component.annotation.ComponentObject;
import org.allaymc.server.component.annotation.Dependency;
import org.allaymc.server.component.annotation.OnInitFinish;
import org.allaymc.server.container.impl.BrewingStandContainerImpl;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.packet.ContainerSetDataPacket;

/**
 * @author IWareQ
 */
@Getter
@Setter
public class BlockEntityBrewingStandBaseComponentImpl extends BlockEntityBaseComponentImpl implements BlockEntityBrewingStandBaseComponent {
    protected static final String TAG_COOK_TIME = "CookTime";
    protected static final String TAG_FUEL_AMOUNT = "FuelAmount";
    protected static final String TAG_FUEL_TOTAL = "FuelTotal";

    protected static final int MAX_BREW_TIME = 400;

    @Dependency
    @Setter(AccessLevel.NONE)
    protected BlockEntityContainerHolderComponent containerHolderComponent;
    @ComponentObject
    protected BlockEntityBrewingStand thisBlockEntity;

    protected int brewTime;
    protected int fuelAmount;
    protected int fuelTotal;

    public BlockEntityBrewingStandBaseComponentImpl(BlockEntityInitInfo initInfo) {
        super(initInfo);
    }

    @OnInitFinish
    @Override
    public void onInitFinish(BlockEntityInitInfo initInfo) {
        super.onInitFinish(initInfo);
        BrewingStandContainerImpl container = containerHolderComponent.getContainer();
        for (int i = 1; i < 4; i++) {
            int finalI = i;
            container.addSlotChangeListener(finalI, item -> {
                var currentBlockState = getDimension().getBlockState(position);
                var values = currentBlockState.getBlockType().getProperties().values();
                //noinspection unchecked
                currentBlockState = currentBlockState.setPropertyValue(values.toArray(new BlockPropertyType[0])[finalI - 1], item != ItemAirStack.AIR_STACK);
                getDimension().setBlockState(
                        position, currentBlockState,
                        0, true, true, false
                );
            });
        }

        container.addSlotChangeListener(BrewingStandContainerImpl.REAGENT_SLOT, item -> {
            brewTime = item == ItemAirStack.AIR_STACK ? 0 : MAX_BREW_TIME;
        });
    }

    @Override
    public void tick(long currentTick) {
        BrewingStandContainerImpl container = containerHolderComponent.getContainer();
        tickBrewingStand(container);
        container.sendContainerData(ContainerSetDataPacket.BREWING_STAND_FUEL_AMOUNT, fuelAmount);
        container.sendContainerData(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL, fuelTotal);
    }

    protected void tickBrewingStand(BrewingStandContainerImpl container) {
        if (!checkFuel(container)) {
            return;
        }

        var reagent = container.getReagent();
        var a = findRecipe(container.getResult(0), reagent);
        var b = findRecipe(container.getResult(1), reagent);
        var c = findRecipe(container.getResult(2), reagent);

        if (a == null && b == null && c == null) {
            brewTime = MAX_BREW_TIME;
            container.sendContainerData(ContainerSetDataPacket.BREWING_STAND_BREW_TIME, 0); // We should reset brew animation
            return;
        }

        if (brewTime == MAX_BREW_TIME) {
            var event = new BrewingStandStartBrewEvent(thisBlockEntity, brewTime);
            if (!event.call()) {
                return;
            }

            brewTime = event.getBrewingTime();
        }

        if (brewTime > 0) {
            if (brewTime % 40 == 0) {
                container.sendContainerData(ContainerSetDataPacket.BREWING_STAND_BREW_TIME, brewTime);
            }

            brewTime--;
            return;
        }

        if (container.isEmpty(BrewingStandContainerImpl.REAGENT_SLOT)) {
            return;
        }

        var result = new ItemStack[3];
        if (a != null) result[0] = a.getOutput().copy();
        if (b != null) result[1] = b.getOutput().copy();
        if (c != null) result[2] = c.getOutput().copy();

        var event = new BrewingStandBrewEvent(thisBlockEntity, result);
        if (!event.call()) {
            return;
        }

        for (int i = 0; i < event.getResult().length; i++) {
            var itemStack = event.getResult()[i];
            if (itemStack != null) {
                container.setResult(i, itemStack);
            }
        }

        if (reagent.getCount() > 1) {
            reagent.reduceCount(1);
            container.notifySlotChange(BrewingStandContainerImpl.REAGENT_SLOT);
        } else {
            container.clearSlot(BrewingStandContainerImpl.REAGENT_SLOT);
        }

        fuelAmount--;
        getDimension().addSound(position, SimpleSound.POTION_BREWED);
    }

    protected boolean checkFuel(BrewingStandContainerImpl container) {
        if (fuelAmount > 0) {
            return true;
        }

        var fuel = container.getFuel();
        if (fuel.getItemType() != ItemTypes.BLAZE_POWDER) {
            return false;
        }

        var event = new BrewingStandConsumeFuelEvent(thisBlockEntity, fuel);
        if (!event.call()) {
            return false;
        }

        if (fuel.getCount() > 1) {
            fuel.reduceCount(1);
            container.notifySlotChange(BrewingStandContainerImpl.FUEL_SLOT);
        } else {
            container.clearSlot(BrewingStandContainerImpl.FUEL_SLOT);
        }

        fuelAmount = 20;
        fuelTotal = fuelAmount;
        return true;
    }

    protected PotionRecipe findRecipe(ItemStack ingredient, ItemStack reagent) {
        return (PotionRecipe) Registries.RECIPES.get(PotionRecipe.buildIdentifier(ingredient, reagent));
    }

    @Override
    public NbtMap saveNBT() {
        return super.saveNBT()
                .toBuilder()
                .putShort(TAG_COOK_TIME, (short) brewTime)
                .putShort(TAG_FUEL_AMOUNT, (short) fuelAmount)
                .putShort(TAG_FUEL_TOTAL, (short) fuelTotal)
                .build();
    }

    @Override
    public void loadNBT(NbtMap nbt) {
        super.loadNBT(nbt);
        nbt.listenForShort(TAG_COOK_TIME, value -> brewTime = value);
        nbt.listenForShort(TAG_FUEL_AMOUNT, value -> fuelAmount = value);
        nbt.listenForShort(TAG_FUEL_TOTAL, value -> fuelTotal = value);
    }
}
