package com.gregtechceu.gtceu.common.machine.multiblock.steam;

import com.gregtechceu.gtceu.api.GTValues;
import com.gregtechceu.gtceu.api.blockentity.BlockEntityCreationInfo;
import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability;
import com.gregtechceu.gtceu.api.capability.recipe.IO;
import com.gregtechceu.gtceu.api.machine.MetaMachine;
import com.gregtechceu.gtceu.api.machine.feature.IMuiMachine;
import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility;
import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine;
import com.gregtechceu.gtceu.api.machine.steam.SteamEnergyRecipeHandler;
import com.gregtechceu.gtceu.api.machine.trait.notifiable.NotifiableFluidTank;
import com.gregtechceu.gtceu.api.machine.trait.recipe.RecipeHandlerList;
import com.gregtechceu.gtceu.api.multiblock.error.PatternStringError;
import com.gregtechceu.gtceu.api.recipe.GTRecipe;
import com.gregtechceu.gtceu.api.recipe.RecipeHelper;
import com.gregtechceu.gtceu.api.recipe.content.ContentModifier;
import com.gregtechceu.gtceu.api.recipe.modifier.ModifierFunction;
import com.gregtechceu.gtceu.api.recipe.modifier.ParallelLogic;
import com.gregtechceu.gtceu.api.recipe.modifier.RecipeModifier;
import com.gregtechceu.gtceu.common.data.GTMaterials;
import com.gregtechceu.gtceu.common.mui.GTMultiblockTextUtil;
import com.gregtechceu.gtceu.config.ConfigHolder;

import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.network.chat.Component;

import brachy.modularui.drawable.GuiTextures;
import brachy.modularui.drawable.Icon;
import brachy.modularui.factory.PosGuiData;
import brachy.modularui.screen.UISettings;
import brachy.modularui.utils.Alignment;
import brachy.modularui.value.sync.PanelSyncManager;
import brachy.modularui.widget.ParentWidget;
import brachy.modularui.widgets.ListWidget;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.Nullable;

import javax.annotation.ParametersAreNonnullByDefault;

@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class SteamParallelMultiblockMachine extends WorkableMultiblockMachine implements IMuiMachine {

    @Getter
    @Setter
    private int maxParallels;

    @Nullable
    private SteamEnergyRecipeHandler steamEnergy = null;

    // if in millibuckets, this is 2.0, Meaning 2mb of steam -> 1 EU
    public static final double CONVERSION_RATE = 2.0;

    public SteamParallelMultiblockMachine(BlockEntityCreationInfo info, int maxParallels) {
        super(info);
        this.maxParallels = maxParallels;
    }

    public SteamParallelMultiblockMachine(BlockEntityCreationInfo info) {
        this(info, ConfigHolder.INSTANCE.machines.steamMultiParallelAmount);
    }

    @Override
    public void invalidateStructure(String name) {
        super.invalidateStructure(name);
        this.steamEnergy = null;
    }

    @Override
    public void formStructure(String substructureName) {
        super.formStructure(substructureName);
        var pState = patternStates.get(substructureName);
        for (var part : getParts()) {
            if (!PartAbility.STEAM.isApplicable(part.getDefinition().getBlock())) continue;
            var handlers = part.getRecipeHandlers();
            for (var hl : handlers) {
                if (!hl.isValid(IO.IN)) continue;
                for (var fluidHandler : hl.getCapability(FluidRecipeCapability.CAP)) {
                    if (!(fluidHandler instanceof NotifiableFluidTank nft)) continue;
                    if (nft.isFluidValid(0, GTMaterials.Steam.getFluid(1))) {
                        steamEnergy = new SteamEnergyRecipeHandler(nft, getConversionRate());
                        addHandlerList(RecipeHandlerList.of(IO.IN, steamEnergy));
                        return;
                    }
                }
            }
        }
        if (steamEnergy == null) { // No steam hatch found
            pState.setError(
                    new PatternStringError(Component.translatable("gtceu.predicate_error.steam.missing_steam_hatch")));
            invalidateStructure(substructureName);
        }
    }

    public double getConversionRate() {
        return CONVERSION_RATE;
    }

    /**
     * Recipe Modifier for <b>Steam Multiblock Machines</b> - can be used as a valid {@link RecipeModifier}
     * <p>
     * Recipe is rejected if tier is greater than LV
     * </p>
     * <p>
     * Recipe is parallelized up to the Multiblock's parallel limit.
     * Then, duration is multiplied by {@code 1.5×} and EUt is multiplied by {@code (8/9) × parallels}, up to a cap of
     * 32 EUt
     * </p>
     *
     * @param machine a {@link SteamParallelMultiblockMachine}
     * @param recipe  recipe
     * @return A {@link ModifierFunction} for the given Steam Multiblock Machine and recipe
     */
    public static ModifierFunction recipeModifier(MetaMachine machine, GTRecipe recipe) {
        if (!(machine instanceof SteamParallelMultiblockMachine steamMachine)) {
            return RecipeModifier.nullWrongType(SteamParallelMultiblockMachine.class, machine);
        }
        if (RecipeHelper.getRecipeEUtTier(recipe) > GTValues.LV) return ModifierFunction.NULL;

        // Duration = 1.5x base duration
        // EUt (not steam) = (4/3) * (2/3) * parallels * base EUt, up to a max of 32 EUt
        long eut = recipe.getInputEUt().getTotalEU();
        int parallelAmount = ParallelLogic.getParallelAmount(machine, recipe, steamMachine.maxParallels);
        double eutMultiplier = (eut * 0.8888 * parallelAmount <= 32) ? (0.8888 * parallelAmount) : (32.0 / eut);
        return ModifierFunction.builder()
                .inputModifier(ContentModifier.multiplier(parallelAmount))
                .outputModifier(ContentModifier.multiplier(parallelAmount))
                .durationMultiplier(1.5)
                .eutMultiplier(eutMultiplier)
                .parallels(parallelAmount)
                .build();
    }

    @Override
    public void buildMainUI(ParentWidget<?> mainWidget, PosGuiData guiData, PanelSyncManager syncManager,
                            UISettings settings) {
        mainWidget.size(170, 76).background(GuiTextures.DISPLAY);

        var listWidget = new ListWidget<>()
                .width(170 - 6)
                .height(76)
                .childSeparator(Icon.EMPTY_2PX)
                .crossAxisAlignment(Alignment.CrossAxis.START)
                .posRel(Alignment.CenterLeft);

        listWidget
                .child(GTMultiblockTextUtil.addUnformedWarning(this, syncManager))
                .child(GTMultiblockTextUtil.addSteamUsageLine(this.steamEnergy, syncManager))
                .child(GTMultiblockTextUtil.addWorkingStatusLine(this, syncManager))
                .child(GTMultiblockTextUtil.addProgressLine(this, syncManager))
                .child(GTMultiblockTextUtil.addParallelLine(this, syncManager))
                .child(GTMultiblockTextUtil.addOutputLines(this, syncManager));

        mainWidget.child(listWidget.left(3).top(3));

        /*
         * 
         * else if (pState.hasError()) {
         * textList.addAll(pState.getError().getErrorInfo());
         * }
         */
    }
}
