package chick.extrabotany.common.brews;

import com.google.common.collect.Lists;
import com.mojang.datafixers.util.Pair;

import net.minecraft.ChatFormatting;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffectUtil;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ItemUtils;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.Level;

import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.brew.Brew;
import vazkii.botania.api.brew.IBrewItem;
import vazkii.botania.common.brew.ModBrews;
import vazkii.botania.common.helper.ItemNBTHelper;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import static vazkii.botania.common.lib.ResourceLocationHelper.prefix;

public class ItemBrewBase extends Item implements IBrewItem
{

    private static final String TAG_BREW_KEY = "brewKey";
    private static final String TAG_SWIGS_LEFT = "swigsLeft";
    private final int maxSwigs;
    private final int swigs;
    private final int drinkSpeed;
    private final Supplier<Item> baseItem;
    private final float multiplier;
    private final int amplifier;

    public ItemBrewBase(Properties builder, int swigs, int drinkSpeed, Supplier<Item> baseItem, float multiplier, int amplifier)
    {
        super(builder);
        this.maxSwigs = swigs;
        this.swigs = swigs;
        this.drinkSpeed = drinkSpeed;
        this.baseItem = baseItem;
        this.multiplier = multiplier;
        this.amplifier = amplifier;
    }

    @Override
    public int getUseDuration(ItemStack stack)
    {
        return drinkSpeed;
    }

    @Nonnull
    @Override
    public UseAnim getUseAnimation(ItemStack stack)
    {
        return UseAnim.DRINK;
    }

    @Nonnull
    @Override
    public InteractionResultHolder<ItemStack> use(Level world, Player player, @Nonnull InteractionHand hand)
    {
        return ItemUtils.startUsingInstantly(world, player, hand);
    }

    @Nonnull
    @Override
    public ItemStack finishUsingItem(@Nonnull ItemStack stack, Level world, LivingEntity living)
    {
        if (!world.isClientSide)
        {
            for (MobEffectInstance effect : getBrew(stack).getPotionEffects(stack))
            {
                MobEffectInstance newEffect = new MobEffectInstance(effect.getEffect(), (int) (effect.getDuration() * multiplier), effect.getAmplifier() + amplifier, true, true);
                if (effect.getEffect().isInstantenous())
                {
                    effect.getEffect().applyInstantenousEffect(living, living, living, newEffect.getAmplifier(), 1F);
                } else
                {
                    living.addEffect(newEffect);
                }
            }

            if (world.random.nextBoolean())
            {
                world.playSound(null, living.getX(), living.getY(), living.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 1F, 1F);
            }

            int swigs = getSwigsLeft(stack);
            if (living instanceof Player player && !player.getAbilities().instabuild)
            {
                //to_do: infinitewine
                //if (swigs == 1 && stack.getItem() != ModItems.infinitewine)
                if (swigs == 1)
                {
                    ItemStack result = getBaseStack();
                    if (!((Player) living).getInventory().add(result))
                    {
                        return result;
                    } else
                    {
                        return ItemStack.EMPTY;
                    }
                }
                setSwigsLeft(stack, swigs - 1);
            }
        }

        return stack;
    }

    @Override
    public void fillItemCategory(CreativeModeTab tab, NonNullList<ItemStack> list)
    {
        if (allowdedIn(tab))
        {
            for (Brew brew : BotaniaAPI.instance().getBrewRegistry())
            {
                if (brew == ModBrews.fallbackBrew)
                {
                    continue;
                }
                ItemStack stack = new ItemStack(this);
                setBrew(stack, brew);
                list.add(stack);
            }
        }
    }

    @Nonnull
    @Override
    public Component getName(@Nonnull ItemStack stack)
    {
        return new TranslatableComponent(getDescriptionId(), new TranslatableComponent(getBrew(stack).getTranslationKey(stack)),
                new TextComponent(Integer.toString(getSwigsLeft(stack))).withStyle(ChatFormatting.BOLD));
    }

    // [VanillaCopy] PotionUtils.addPotionTooltip, with custom effect list
    @OnlyIn(Dist.CLIENT)
    public void addPotionTooltip(List<MobEffectInstance> list, List<Component> lores, float durationFactor)
    {
        List<Pair<Attribute, AttributeModifier>> list1 = Lists.newArrayList();
        if (list.isEmpty())
        {
            lores.add((new TranslatableComponent("effect.none")).withStyle(ChatFormatting.GRAY));
        } else
        {
            for (MobEffectInstance effectinstance : list)
            {
                MutableComponent iformattabletextcomponent = new TranslatableComponent(effectinstance.getDescriptionId());
                MobEffect effect = effectinstance.getEffect();
                Map<Attribute, AttributeModifier> map = effect.getAttributeModifiers();
                if (!map.isEmpty())
                {
                    for (Map.Entry<Attribute, AttributeModifier> entry : map.entrySet())
                    {
                        AttributeModifier attributemodifier = entry.getValue();
                        AttributeModifier attributemodifier1 = new AttributeModifier(attributemodifier.getName(), effect.getAttributeModifierValue(effectinstance.getAmplifier(), attributemodifier), attributemodifier.getOperation());
                        list1.add(new Pair<>(entry.getKey(), attributemodifier1));
                    }
                }

                if (effectinstance.getAmplifier() > 0)
                {
                    iformattabletextcomponent = new TranslatableComponent("potion.withAmplifier", iformattabletextcomponent, new TranslatableComponent("potion.potency." + (effectinstance.getAmplifier() + amplifier)));
                }

                if (effectinstance.getDuration() > 20)
                {
                    iformattabletextcomponent = new TranslatableComponent("potion.withDuration", iformattabletextcomponent, MobEffectUtil.formatDuration(new MobEffectInstance(effectinstance.getEffect(), (int) (effectinstance.getDuration() * multiplier)), durationFactor));
                }

                lores.add(iformattabletextcomponent.withStyle(effect.getCategory().getTooltipFormatting()));
            }
        }

        if (!list1.isEmpty())
        {
            lores.add(TextComponent.EMPTY);
            lores.add((new TranslatableComponent("potion.whenDrank")).withStyle(ChatFormatting.DARK_PURPLE));

            for (Pair<Attribute, AttributeModifier> pair : list1)
            {
                AttributeModifier attributemodifier2 = pair.getSecond();
                double d0 = attributemodifier2.getAmount();
                double d1;
                if (attributemodifier2.getOperation() != AttributeModifier.Operation.MULTIPLY_BASE && attributemodifier2.getOperation() != AttributeModifier.Operation.MULTIPLY_TOTAL)
                {
                    d1 = attributemodifier2.getAmount();
                } else
                {
                    d1 = attributemodifier2.getAmount() * 100.0D;
                }

                if (d0 > 0.0D)
                {
                    lores.add((new TranslatableComponent("attribute.modifier.plus." + attributemodifier2.getOperation().toValue(), ItemStack.ATTRIBUTE_MODIFIER_FORMAT.format(d1), new TranslatableComponent(pair.getFirst().getDescriptionId()))).withStyle(ChatFormatting.BLUE));
                } else if (d0 < 0.0D)
                {
                    d1 = d1 * -1.0D;
                    lores.add((new TranslatableComponent("attribute.modifier.take." + attributemodifier2.getOperation().toValue(), ItemStack.ATTRIBUTE_MODIFIER_FORMAT.format(d1), new TranslatableComponent(pair.getFirst().getDescriptionId()))).withStyle(ChatFormatting.RED));
                }
            }
        }
    }

    @OnlyIn(Dist.CLIENT)
    @Override
    public void appendHoverText(ItemStack stack, Level world, List<Component> list, TooltipFlag flags)
    {
        addPotionTooltip(getBrew(stack).getPotionEffects(stack), list, 1);
    }

    @Override
    public Brew getBrew(ItemStack stack)
    {
        String key = ItemNBTHelper.getString(stack, TAG_BREW_KEY, "");
        return BotaniaAPI.instance().getBrewRegistry().get(ResourceLocation.tryParse(key));
    }

    public static void setBrew(ItemStack stack, @Nullable Brew brew)
    {
        ResourceLocation id;
        if (brew != null)
        {
            id = BotaniaAPI.instance().getBrewRegistry().getKey(brew);
        } else
        {
            id = prefix("fallback");
        }
        setBrew(stack, id);
    }

    public static void setBrew(ItemStack stack, ResourceLocation brew)
    {
        ItemNBTHelper.setString(stack, TAG_BREW_KEY, brew.toString());
    }

    @Nonnull
    public static String getSubtype(ItemStack stack)
    {
        return stack.hasTag() ? ItemNBTHelper.getString(stack, TAG_BREW_KEY, "none") : "none";
    }

    public int getSwigs()
    {
        return swigs;
    }

    public int getSwigsLeft(ItemStack stack)
    {
        return ItemNBTHelper.getInt(stack, TAG_SWIGS_LEFT, swigs);
    }

    public void setSwigsLeft(ItemStack stack, int swigs)
    {
        ItemNBTHelper.setInt(stack, TAG_SWIGS_LEFT, swigs);
        ItemNBTHelper.setInt(stack, "CustomModelData", maxSwigs - swigs);
    }

    public ItemStack getBaseStack()
    {
        return new ItemStack(baseItem.get());
    }
}
