package com.acethewildfire.acesbs.datagen;

import com.acethewildfire.acesbs.block.ModBlocks;
import com.acethewildfire.acesbs.block.custom.TobaccoCrop;
import com.acethewildfire.acesbs.item.ModItems;
import com.acethewildfire.acesbs.item.PrismaSteelItems;
import com.acethewildfire.acesbs.util.ModTags;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.minecraft.block.Block;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.item.Item;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.condition.BlockStatePropertyLootCondition;
import net.minecraft.loot.condition.InvertedLootCondition;
import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.condition.MatchToolLootCondition;
import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.entry.LeafEntry;
import net.minecraft.loot.function.ApplyBonusLootFunction;
import net.minecraft.loot.function.ExplosionDecayLootFunction;
import net.minecraft.loot.function.SetCountLootFunction;
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
import net.minecraft.loot.provider.number.UniformLootNumberProvider;
import net.minecraft.predicate.NumberRange;
import net.minecraft.predicate.StatePredicate;
import net.minecraft.predicate.item.EnchantmentPredicate;
import net.minecraft.predicate.item.EnchantmentsPredicate;
import net.minecraft.predicate.item.ItemPredicate;
import net.minecraft.predicate.item.ItemSubPredicateTypes;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper;

import java.util.List;
import java.util.concurrent.CompletableFuture;

public class ModLootTableProvider extends FabricBlockLootTableProvider {

    private boolean isValidItem(ItemStack stack) {
        return stack.isIn(ModTags.Items.PRISMA_CONVERTABLE);
    }

    public ModLootTableProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registryLookup) {
        super(dataOutput, registryLookup);
    }

    @Override
    public void generate() {
        addDrop(ModBlocks.ENTROPY_BLOCK);
        addDrop(ModBlocks.INFERNAL_ASHES_BLOCK);
        addDrop(ModBlocks.STABLE_ENTROPY_BLOCK);
        addDrop(ModBlocks.PRISMA_STEEL_BLOCK);
        addDrop(ModBlocks.ENTROPIC_STABILIZER);
        addDrop(ModBlocks.ENTROPIC_EVISCERATOR);
        addDrop(ModBlocks.ENTROPIC_ENTANGLER);

        addDropWithSilkTouch(ModBlocks.CRYSTAL_ENTROPY);
        addDropWithSilkTouch(ModBlocks.CRYSTAL_ENTROPY_PANE);


        addDrop(ModBlocks.LEMONWOOD_LOG);
        addDrop(ModBlocks.STRIPPED_LEMONWOOD_LOG);
        addDrop(ModBlocks.LEMONWOOD_WOOD);
        addDrop(ModBlocks.STRIPPED_LEMONWOOD_WOOD);
        addDrop(ModBlocks.LEMONWOOD_SAPLING);
        addDrop(ModBlocks.LEMONWOOD_LEAVES, leavesDrops(ModBlocks.LEMONWOOD_LEAVES, ModBlocks.LEMONWOOD_SAPLING, 0.0625f));

        addDrop(ModBlocks.LEMONWOOD_PLANKS);
        addDrop(ModBlocks.LEMONWOOD_STAIRS);
        addDrop(ModBlocks.LEMONWOOD_SLAB, slabDrops(ModBlocks.LEMONWOOD_SLAB));
        addDrop(ModBlocks.LEMONWOOD_BUTTON);
        addDrop(ModBlocks.LEMONWOOD_PRESSURE_PLATE);
        addDrop(ModBlocks.LEMONWOOD_FENCE);
        addDrop(ModBlocks.LEMONWOOD_FENCE_GATE);
        addDrop(ModBlocks.LEMONWOOD_WALL);
        addDrop(ModBlocks.LEMONWOOD_DOOR, doorDrops(ModBlocks.LEMONWOOD_DOOR));
        addDrop(ModBlocks.LEMONWOOD_TRAPDOOR);

        addDrop(ModBlocks.ENTROPY_ORE, prismaSteelOreDrops(ModBlocks.ENTROPY_ORE, ModItems.RAW_ENTROPY, 1, 3));
        addDrop(ModBlocks.DEEPSLATE_ENTROPY_ORE, prismaSteelOreDrops(ModBlocks.DEEPSLATE_ENTROPY_ORE, ModItems.RAW_ENTROPY, 1, 3));
        addDrop(ModBlocks.INFERNAL_ASHES_ORE, prismaSteelOreDrops(ModBlocks.INFERNAL_ASHES_ORE, ModItems.INFERNAL_ASHES, 1, 3));
        addDrop(ModBlocks.ENDTROPY_ORE, prismaSteelOreDrops(ModBlocks.ENDTROPY_ORE, ModItems.RAW_ENDTROPY, 1, 3));

        BlockStatePropertyLootCondition.Builder builder2 = BlockStatePropertyLootCondition.builder(ModBlocks.TOBACCO_CROP)
                .properties(StatePredicate.Builder.create().exactMatch(TobaccoCrop.AGE, TobaccoCrop.MAX_AGE));
        this.addDrop(ModBlocks.TOBACCO_CROP, this.cropDrops(ModBlocks.TOBACCO_CROP, ModItems.TOBACCO, ModItems.TOBACCO_SEEDS, builder2));
    }

    public LootTable.Builder multipleOreDrops(Block drop, Item item, float minDrops, float maxDrops) {
        RegistryWrapper.Impl<Enchantment> impl = this.registryLookup.getWrapperOrThrow(RegistryKeys.ENCHANTMENT);
        return this.dropsWithSilkTouch(
                drop,
                this.applyExplosionDecay(drop, ((LeafEntry.Builder<?>)ItemEntry.builder(item)
                        .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(minDrops, maxDrops))))
                        .apply(ApplyBonusLootFunction.oreDrops(impl.getOrThrow(Enchantments.FORTUNE)))));
    }

    public LootCondition.Builder specialToolCondition(ItemConvertible... tools){
        return MatchToolLootCondition.builder(
                ItemPredicate.Builder.create()
                        .items(tools)
        );
    }


    public LootTable.Builder prismaSteelOreDrops(Block block, Item defaultItem, float minDrops, float maxDrops){
        RegistryWrapper.Impl<Enchantment> impl = this.registryLookup.getWrapperOrThrow(RegistryKeys.ENCHANTMENT);
        LootCondition.Builder silkTouchCondition = MatchToolLootCondition.builder(
                ItemPredicate.Builder.create()
                        .subPredicate(
                                ItemSubPredicateTypes.ENCHANTMENTS,
                                EnchantmentsPredicate.enchantments(List.of(new EnchantmentPredicate(impl.getOrThrow(Enchantments.SILK_TOUCH), NumberRange.IntRange.atLeast(1))))
                        )
        );

        LootPool.Builder defaultPool = LootPool.builder()
                .rolls(UniformLootNumberProvider.create(minDrops, maxDrops))
                .conditionally(InvertedLootCondition.builder(silkTouchCondition))
                .conditionally(InvertedLootCondition.builder(specialToolCondition(PrismaSteelItems.PRISMA_STEEL_PICKAXE, PrismaSteelItems.PRISMA_STEEL_HAMMER)))
                .with(ItemEntry.builder(defaultItem).weight(1))
                .apply(ApplyBonusLootFunction.oreDrops(impl.getOrThrow(Enchantments.FORTUNE)))
                .apply(ExplosionDecayLootFunction.builder());

        LootPool.Builder silkTouchPool = LootPool.builder()
                .rolls(ConstantLootNumberProvider.create(1))
                .conditionally(silkTouchCondition)
                .with(ItemEntry.builder(block.asItem()).weight(1))
                .apply(ExplosionDecayLootFunction.builder());

        LootPool.Builder prismaPool = LootPool.builder()
                .rolls(UniformLootNumberProvider.create(minDrops, maxDrops))
                .conditionally(InvertedLootCondition.builder(silkTouchCondition))
                .conditionally(specialToolCondition(PrismaSteelItems.PRISMA_STEEL_PICKAXE, PrismaSteelItems.PRISMA_STEEL_HAMMER))
                .with(ItemEntry.builder(Items.AIR).weight(360))
                .with(ItemEntry.builder(Items.COAL).weight(12))
                .with(ItemEntry.builder(Items.RAW_COPPER).weight(10))
                .with(ItemEntry.builder(Items.RAW_IRON).weight(6))
                .with(ItemEntry.builder(Items.RAW_GOLD).weight(4))
                .with(ItemEntry.builder(Items.LAPIS_LAZULI).weight(3))
                .with(ItemEntry.builder(Items.REDSTONE).weight(2))
                .with(ItemEntry.builder(ModItems.RAW_ENTROPY).weight(1))
                .with(ItemEntry.builder(Items.DIAMOND).weight(1))
                .with(ItemEntry.builder(Items.EMERALD).weight(1))
                .apply(ApplyBonusLootFunction.oreDrops(impl.getOrThrow(Enchantments.FORTUNE)))
                .apply(ExplosionDecayLootFunction.builder());

        return LootTable.builder()
                .pool(silkTouchPool)
                .pool(prismaPool)
                .pool(defaultPool);
    }
}
