package com.acethewildfire.acesbs.datagen;

import com.acethewildfire.acesbs.block.FireOakBlocks;
import com.acethewildfire.acesbs.block.LemonWoodBlocks;
import com.acethewildfire.acesbs.block.ModBlocks;
import com.acethewildfire.acesbs.block.custom.TobaccoCrop;
import com.acethewildfire.acesbs.item.FractalDiamondItems;
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.*;
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.component.ComponentPredicateTypes;
import net.minecraft.predicate.component.ComponentsPredicate;
import net.minecraft.predicate.item.EnchantmentPredicate;
import net.minecraft.predicate.item.EnchantmentsPredicate;
import net.minecraft.predicate.item.ItemPredicate;
import net.minecraft.registry.RegistryEntryLookup;
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.RAW_ENTROPY_BLOCK);
        addDrop(ModBlocks.INFERNAL_ASHES_BLOCK);
        addDrop(ModBlocks.STABLE_ENTROPY_BLOCK);
        addDrop(ModBlocks.RAW_ENDTROPY_BLOCK);
        addDrop(ModBlocks.STABLE_ENDTROPY_BLOCK);
        addDrop(ModBlocks.PRISMA_STEEL_BLOCK);
        addDrop(ModBlocks.ASHEN_STEEL_BLOCK);
        addDrop(ModBlocks.FRACTAL_DIAMOND_BLOCK);
        addDrop(ModBlocks.HELLFIRE_DIAMOND_BLOCK);
        addDrop(ModBlocks.ENTROPIC_STABILIZER);
        addDrop(ModBlocks.ENTROPIC_EVISCERATOR);
        addDrop(ModBlocks.ENTROPIC_ENTANGLER);

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

        addDrop(ModBlocks.LIZARD_PLANKS);
        addDrop(ModBlocks.LIZARD_BUTTON);

        addDrop(LemonWoodBlocks.LEMONWOOD_LOG);
        addDrop(LemonWoodBlocks.STRIPPED_LEMONWOOD_LOG);
        addDrop(LemonWoodBlocks.LEMONWOOD_WOOD);
        addDrop(LemonWoodBlocks.STRIPPED_LEMONWOOD_WOOD);
        addDrop(LemonWoodBlocks.LEMONWOOD_SAPLING);
        addDrop(LemonWoodBlocks.LEMONWOOD_LEAVES, lemonwoodLeavesDrops(LemonWoodBlocks.LEMONWOOD_LEAVES, LemonWoodBlocks.LEMONWOOD_SAPLING, 0.0625f));

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

        addDrop(FireOakBlocks.FIRE_OAK_LOG);
        addDrop(FireOakBlocks.STRIPPED_FIRE_OAK_LOG);
        addDrop(FireOakBlocks.FIRE_OAK_WOOD);
        addDrop(FireOakBlocks.STRIPPED_FIRE_OAK_WOOD);
        addDrop(FireOakBlocks.FIRE_OAK_SAPLING);
        addDrop(FireOakBlocks.FIRE_OAK_LEAVES, leavesDrops(FireOakBlocks.FIRE_OAK_LEAVES, FireOakBlocks.FIRE_OAK_SAPLING, 0.0625f));

        addDrop(FireOakBlocks.FIRE_OAK_PLANKS);
        addDrop(FireOakBlocks.FIRE_OAK_STAIRS);
        addDrop(FireOakBlocks.FIRE_OAK_SLAB, slabDrops(FireOakBlocks.FIRE_OAK_SLAB));
        addDrop(FireOakBlocks.FIRE_OAK_BUTTON);
        addDrop(FireOakBlocks.FIRE_OAK_PRESSURE_PLATE);
        addDrop(FireOakBlocks.FIRE_OAK_FENCE);
        addDrop(FireOakBlocks.FIRE_OAK_FENCE_GATE);
        addDrop(FireOakBlocks.FIRE_OAK_WALL);
        addDrop(FireOakBlocks.FIRE_OAK_DOOR, doorDrops(FireOakBlocks.FIRE_OAK_DOOR));
        addDrop(FireOakBlocks.FIRE_OAK_TRAPDOOR);

        addDrop(ModBlocks.ORDERED_CORE);

        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 lemonwoodLeavesDrops(Block leaves, Block sapling, float... saplingChance) {
        RegistryWrapper.Impl<Enchantment> impl = this.registries.getOrThrow(RegistryKeys.ENCHANTMENT);
        return this.leavesDrops(leaves, sapling, saplingChance).pool(LootPool.builder().rolls(ConstantLootNumberProvider.create(1.0F)).conditionally(this.createWithoutShearsOrSilkTouchCondition()).with(((LeafEntry.Builder)this.addSurvivesExplosionCondition(leaves, ItemEntry.builder(ModItems.LEMON))).conditionally(TableBonusLootCondition.builder(impl.getOrThrow(Enchantments.FORTUNE), new float[]{0.005F, 0.0055555557F, 0.00625F, 0.008333334F, 0.025F}))));
    }

    public LootTable.Builder multipleOreDrops(Block drop, Item item, float minDrops, float maxDrops) {
        RegistryWrapper.Impl<Enchantment> impl = this.registries.getOrThrow(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(RegistryEntryLookup<Item> lookup, ItemConvertible... tools) {
        return MatchToolLootCondition.builder(
                ItemPredicate.Builder.create()
                        .items(lookup, tools)
        );
    }


    public LootTable.Builder prismaSteelOreDrops(Block block, Item defaultItem, float minDrops, float maxDrops){
        RegistryWrapper.Impl<Enchantment> impl = this.registries.getOrThrow(RegistryKeys.ENCHANTMENT);
        RegistryEntryLookup<Item> itemLookup = registries.getOrThrow(RegistryKeys.ITEM);
        LootCondition.Builder silkTouchCondition = MatchToolLootCondition.builder(
                ItemPredicate.Builder.create().components(
                        ComponentsPredicate.Builder.create()
                                .partial(
                                        ComponentPredicateTypes.ENCHANTMENTS,
                                        EnchantmentsPredicate.enchantments(
                                                List.of(new EnchantmentPredicate(impl.getOrThrow(Enchantments.SILK_TOUCH), NumberRange.IntRange.atLeast(1)))))
                                .build()
                )
        );

        LootPool.Builder defaultPool = LootPool.builder()
                .rolls(UniformLootNumberProvider.create(minDrops, maxDrops))
                .conditionally(InvertedLootCondition.builder(silkTouchCondition))
//                .conditionally(InvertedLootCondition.builder(specialToolCondition(itemLookup, 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(ConstantLootNumberProvider.create(1)) // only 1 roll
                .conditionally(RandomChanceLootCondition.builder(0.1f)) // chance to drop anything
                .conditionally(InvertedLootCondition.builder(silkTouchCondition))
                .conditionally(specialToolCondition(itemLookup, PrismaSteelItems.PRISMA_STEEL_PICKAXE, PrismaSteelItems.PRISMA_STEEL_HAMMER, FractalDiamondItems.FRACTAL_DIAMOND_PICKAXE, FractalDiamondItems.FRACTAL_DIAMOND_HAMMER))
                .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);
    }
}
