package top.r3944realms.lib39.util.villager;

import com.google.common.collect.Lists;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.npc.VillagerDataHolder;
import net.minecraft.world.entity.npc.VillagerTrades;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.world.item.*;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.alchemy.PotionUtils;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.EnchantmentInstance;
import net.minecraft.world.item.trading.MerchantOffer;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.saveddata.maps.MapDecoration;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;

/**
 * 村民交易构建器
 * 用于创建自定义的交易项目
 */
public class TradeBuilder {
    /**
     * 创建物品换绿宝石的交易
     *
     * @param item       物品
     * @param cost       需要的数量
     * @param maxUses    最大使用次数
     * @param villagerXp 村民获得的经验
     * @return 交易实例 emerald for items
     */
    @Contract("_, _, _, _ -> new")
    public static @NotNull EmeraldForItems createSellItemTrade(ItemLike item, int cost, int maxUses, int villagerXp) {
        return new EmeraldForItems(item, cost, maxUses, villagerXp);
    }

    /**
     * 创建绿宝石换物品的交易
     *
     * @param item          物品
     * @param emeraldCost   需要的绿宝石数量
     * @param numberOfItems 获得的物品数量
     * @param maxUses       最大使用次数
     * @param villagerXp    村民获得的经验
     * @return 交易实例 items for emeralds
     */
    @Contract("_, _, _, _, _ -> new")
    public static @NotNull ItemsForEmeralds createBuyItemTrade(ItemLike item, int emeraldCost, int numberOfItems, int maxUses, int villagerXp) {
        return new ItemsForEmeralds(item, emeraldCost, numberOfItems, maxUses, villagerXp);
    }

    /**
     * 创建物品和绿宝石换物品的交易
     *
     * @param fromItem    提供的物品
     * @param fromCount   提供的物品数量
     * @param emeraldCost 需要的绿宝石数量
     * @param toItem      获得的物品
     * @param toCount     获得的物品数量
     * @param maxUses     最大使用次数
     * @param villagerXp  村民获得的经验
     * @return 交易实例 items and emeralds to items
     */
    @Contract("_, _, _, _, _, _, _ -> new")
    public static @NotNull ItemsAndEmeraldsToItems createItemExchangeTrade(
            ItemLike fromItem, int fromCount, int emeraldCost,
            @NotNull ItemLike toItem, int toCount, int maxUses, int villagerXp) {
        return new ItemsAndEmeraldsToItems(fromItem, fromCount, emeraldCost,
                toItem.asItem(), toCount, maxUses, villagerXp);
    }

    /**
     * 创建附魔物品交易
     *
     * @param item            物品
     * @param baseEmeraldCost 基础绿宝石价格
     * @param maxUses         最大使用次数
     * @param villagerXp      村民获得的经验
     * @return 交易实例 enchanted item for emeralds
     */
    @Contract("_, _, _, _ -> new")
    public static @NotNull EnchantedItemForEmeralds createEnchantedItemTrade(
            Item item, int baseEmeraldCost, int maxUses, int villagerXp) {
        return new EnchantedItemForEmeralds(item, baseEmeraldCost, maxUses, villagerXp);
    }

    /**
     * 创建附魔书交易
     *
     * @param villagerXp 村民获得的经验
     * @return 交易实例 enchant book for emeralds
     */
    @Contract(value = "_ -> new", pure = true)
    public static @NotNull EnchantBookForEmeralds createEnchantedBookTrade(int villagerXp) {
        return new EnchantBookForEmeralds(villagerXp);
    }

    /**
     * 创建宝藏地图交易
     *
     * @param emeraldCost    绿宝石价格
     * @param destination    the destination
     * @param displayName    显示名称
     * @param destinationTyp the destination typ
     * @param maxUses        最大使用次数
     * @param villagerXp     村民获得的经验
     * @return 交易实例 treasure map for emeralds
     */
    @Contract(pure = true)
    public static @NotNull TreasureMapForEmeralds createTreasureMapTrade(
            int emeraldCost, TagKey<Structure> destination, String displayName, MapDecoration.Type destinationTyp, int maxUses, int villagerXp) {

        return new TreasureMapForEmeralds(emeraldCost, destination, displayName, destinationTyp, maxUses, villagerXp);
    }

    /**
     * 创建染色盔甲交易
     *
     * @param item       盔甲物品
     * @param value      绿宝石价格
     * @param maxUses    最大使用次数
     * @param villagerXp 村民获得的经验
     * @return 交易实例 dyed armor for emeralds
     */
    @Contract(value = "_, _, _, _ -> new", pure = true)
    public static @NotNull DyedArmorForEmeralds createDyedArmorTrade(
            Item item, int value, int maxUses, int villagerXp) {
        return new DyedArmorForEmeralds(item, value, maxUses, villagerXp);
    }

    /**
     * The type Emerald for items.
     */
    public static class EmeraldForItems implements VillagerTrades.ItemListing {
        private final Item item;
        private final int cost;
        private final int maxUses;
        private final int villagerXp;
        private final float priceMultiplier;

        /**
         * Instantiates a new Emerald for items.
         *
         * @param item       the item
         * @param cost       the cost
         * @param maxUses    the max uses
         * @param villagerXp the villager xp
         */
        public EmeraldForItems(@NotNull ItemLike item, int cost, int maxUses, int villagerXp) {
            this.item = item.asItem();
            this.cost = cost;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
            this.priceMultiplier = 0.05F;
        }

        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            ItemStack itemstack = new ItemStack(this.item, this.cost);
            return new MerchantOffer(itemstack, new ItemStack(Items.EMERALD), this.maxUses, this.villagerXp, this.priceMultiplier);
        }
    }

    /**
     * The type Items for emeralds.
     */
    public static class ItemsForEmeralds implements VillagerTrades.ItemListing {
        private final ItemStack itemStack;
        private final int emeraldCost;
        private final int numberOfItems;
        private final int maxUses;
        private final int villagerXp;
        private final float priceMultiplier;

        /**
         * Instantiates a new Items for emeralds.
         *
         * @param block         the block
         * @param emeraldCost   the emerald cost
         * @param numberOfItems the number of items
         * @param maxUses       the max uses
         * @param villagerXp    the villager xp
         */
        public ItemsForEmeralds(Block block, int emeraldCost, int numberOfItems, int maxUses, int villagerXp) {
            this(new ItemStack(block), emeraldCost, numberOfItems, maxUses, villagerXp);
        }

        /**
         * Instantiates a new Items for emeralds.
         *
         * @param item          the item
         * @param emeraldCost   the emerald cost
         * @param numberOfItems the number of items
         * @param villagerXp    the villager xp
         */
        public ItemsForEmeralds(ItemLike item, int emeraldCost, int numberOfItems, int villagerXp) {
            this(new ItemStack(item), emeraldCost, numberOfItems, 12, villagerXp);
        }

        /**
         * Instantiates a new Items for emeralds.
         *
         * @param item          the item
         * @param emeraldCost   the emerald cost
         * @param numberOfItems the number of items
         * @param maxUses       the max uses
         * @param villagerXp    the villager xp
         */
        public ItemsForEmeralds(ItemLike item, int emeraldCost, int numberOfItems, int maxUses, int villagerXp) {
            this(new ItemStack(item), emeraldCost, numberOfItems, maxUses, villagerXp);
        }

        /**
         * Instantiates a new Items for emeralds.
         *
         * @param itemStack     the item stack
         * @param emeraldCost   the emerald cost
         * @param numberOfItems the number of items
         * @param maxUses       the max uses
         * @param villagerXp    the villager xp
         */
        public ItemsForEmeralds(ItemStack itemStack, int emeraldCost, int numberOfItems, int maxUses, int villagerXp) {
            this(itemStack, emeraldCost, numberOfItems, maxUses, villagerXp, 0.05F);
        }

        /**
         * Instantiates a new Items for emeralds.
         *
         * @param itemStack       the item stack
         * @param emeraldCost     the emerald cost
         * @param numberOfItems   the number of items
         * @param maxUses         the max uses
         * @param villagerXp      the villager xp
         * @param priceMultiplier the price multiplier
         */
        public ItemsForEmeralds(ItemStack itemStack, int emeraldCost, int numberOfItems, int maxUses, int villagerXp, float priceMultiplier) {
            this.itemStack = itemStack;
            this.emeraldCost = emeraldCost;
            this.numberOfItems = numberOfItems;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
            this.priceMultiplier = priceMultiplier;
        }

        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            return new MerchantOffer(new ItemStack(Items.EMERALD, this.emeraldCost), new ItemStack(this.itemStack.getItem(), this.numberOfItems), this.maxUses, this.villagerXp, this.priceMultiplier);
        }
    }

    /**
     * The type Suspicious stew for emerald.
     */
    public static class SuspiciousStewForEmerald implements VillagerTrades.ItemListing {
        /**
         * The Effect.
         */
        final MobEffect effect;
        /**
         * The Duration.
         */
        final int duration;
        /**
         * The Xp.
         */
        final int xp;
        private final float priceMultiplier;

        /**
         * Instantiates a new Suspicious stew for emerald.
         *
         * @param effect   the effect
         * @param duration the duration
         * @param xp       the xp
         */
        public SuspiciousStewForEmerald(MobEffect effect, int duration, int xp) {
            this.effect = effect;
            this.duration = duration;
            this.xp = xp;
            this.priceMultiplier = 0.05F;
        }

        @Nullable
        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            ItemStack itemstack = new ItemStack(Items.SUSPICIOUS_STEW, 1);
            SuspiciousStewItem.saveMobEffect(itemstack, this.effect, this.duration);
            return new MerchantOffer(new ItemStack(Items.EMERALD, 1), itemstack, 12, this.xp, this.priceMultiplier);
        }
    }

    /**
     * The type Items and emeralds to items.
     */
    public static class ItemsAndEmeraldsToItems implements VillagerTrades.ItemListing {
        private final ItemStack fromItem;
        private final int fromCount;
        private final int emeraldCost;
        private final ItemStack toItem;
        private final int toCount;
        private final int maxUses;
        private final int villagerXp;
        private final float priceMultiplier;

        /**
         * Instantiates a new Items and emeralds to items.
         *
         * @param fromItem   the from item
         * @param fromCount  the from count
         * @param toItem     the to item
         * @param toCount    the to count
         * @param maxUses    the max uses
         * @param villagerXp the villager xp
         */
        public ItemsAndEmeraldsToItems(ItemLike fromItem, int fromCount, Item toItem, int toCount, int maxUses, int villagerXp) {
            this(fromItem, fromCount, 1, toItem, toCount, maxUses, villagerXp);
        }

        /**
         * Instantiates a new Items and emeralds to items.
         *
         * @param fromItem    the from item
         * @param fromCount   the from count
         * @param emeraldCost the emerald cost
         * @param toItem      the to item
         * @param toCount     the to count
         * @param maxUses     the max uses
         * @param villagerXp  the villager xp
         */
        public ItemsAndEmeraldsToItems(ItemLike fromItem, int fromCount, int emeraldCost, Item toItem, int toCount, int maxUses, int villagerXp) {
            this.fromItem = new ItemStack(fromItem);
            this.fromCount = fromCount;
            this.emeraldCost = emeraldCost;
            this.toItem = new ItemStack(toItem);
            this.toCount = toCount;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
            this.priceMultiplier = 0.05F;
        }

        @Nullable
        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            return new MerchantOffer(new ItemStack(Items.EMERALD, this.emeraldCost), new ItemStack(this.fromItem.getItem(), this.fromCount), new ItemStack(this.toItem.getItem(), this.toCount), this.maxUses, this.villagerXp, this.priceMultiplier);
        }
    }

    /**
     * The type Enchanted item for emeralds.
     */
    public static class EnchantedItemForEmeralds implements VillagerTrades.ItemListing {
        private final ItemStack itemStack;
        private final int baseEmeraldCost;
        private final int maxUses;
        private final int villagerXp;
        private final float priceMultiplier;

        /**
         * Instantiates a new Enchanted item for emeralds.
         *
         * @param item            the item
         * @param baseEmeraldCost the base emerald cost
         * @param maxUses         the max uses
         * @param villagerXp      the villager xp
         */
        public EnchantedItemForEmeralds(Item item, int baseEmeraldCost, int maxUses, int villagerXp) {
            this(item, baseEmeraldCost, maxUses, villagerXp, 0.05F);
        }

        /**
         * Instantiates a new Enchanted item for emeralds.
         *
         * @param item            the item
         * @param baseEmeraldCost the base emerald cost
         * @param maxUses         the max uses
         * @param villagerXp      the villager xp
         * @param priceMultiplier the price multiplier
         */
        public EnchantedItemForEmeralds(Item item, int baseEmeraldCost, int maxUses, int villagerXp, float priceMultiplier) {
            this.itemStack = new ItemStack(item);
            this.baseEmeraldCost = baseEmeraldCost;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
            this.priceMultiplier = priceMultiplier;
        }

        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            int i = 5 + random.nextInt(15);
            ItemStack itemstack = EnchantmentHelper.enchantItem(random, new ItemStack(this.itemStack.getItem()), i, false);
            int j = Math.min(this.baseEmeraldCost + i, 64);
            ItemStack itemstack1 = new ItemStack(Items.EMERALD, j);
            return new MerchantOffer(itemstack1, itemstack, this.maxUses, this.villagerXp, this.priceMultiplier);
        }
    }

    /**
     * The type Emeralds for villager type item.
     */
    public static class EmeraldsForVillagerTypeItem implements VillagerTrades.ItemListing {
        private final Map<VillagerType, Item> trades;
        private final int cost;
        private final int maxUses;
        private final int villagerXp;

        /**
         * Instantiates a new Emeralds for villager type item.
         *
         * @param cost       the cost
         * @param maxUses    the max uses
         * @param villagerXp the villager xp
         * @param trades     the trades
         */
        public EmeraldsForVillagerTypeItem(int cost, int maxUses, int villagerXp, Map<VillagerType, Item> trades) {
            BuiltInRegistries.VILLAGER_TYPE.stream().filter((villagerType) -> {
                return !trades.containsKey(villagerType);
            }).findAny().ifPresent((p_258962_) -> {
                throw new IllegalStateException("Missing trade for villager type: " + BuiltInRegistries.VILLAGER_TYPE.getKey(p_258962_));
            });
            this.trades = trades;
            this.cost = cost;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
        }

        @Nullable
        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            if (trader instanceof VillagerDataHolder) {
                ItemStack itemstack = new ItemStack(this.trades.get(((VillagerDataHolder)trader).getVillagerData().getType()), this.cost);
                return new MerchantOffer(itemstack, new ItemStack(Items.EMERALD), this.maxUses, this.villagerXp, 0.05F);
            } else {
                return null;
            }
        }
    }

    /**
     * The type Tipped arrow for items and emeralds.
     */
    @SuppressWarnings("deprecation")
    public static class TippedArrowForItemsAndEmeralds implements VillagerTrades.ItemListing {
        private final ItemStack toItem;
        private final int toCount;
        private final int emeraldCost;
        private final int maxUses;
        private final int villagerXp;
        private final Item fromItem;
        private final int fromCount;
        private final float priceMultiplier;

        /**
         * Instantiates a new Tipped arrow for items and emeralds.
         *
         * @param fromItem    the from item
         * @param fromCount   the from count
         * @param toItem      the to item
         * @param toCount     the to count
         * @param emeraldCost the emerald cost
         * @param maxUses     the max uses
         * @param villagerXp  the villager xp
         */
        public TippedArrowForItemsAndEmeralds(Item fromItem, int fromCount, Item toItem, int toCount, int emeraldCost, int maxUses, int villagerXp) {
            this.toItem = new ItemStack(toItem);
            this.emeraldCost = emeraldCost;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
            this.fromItem = fromItem;
            this.fromCount = fromCount;
            this.toCount = toCount;
            this.priceMultiplier = 0.05F;
        }

        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            ItemStack itemstack = new ItemStack(Items.EMERALD, this.emeraldCost);
            List<Potion> list = BuiltInRegistries.POTION.stream().filter((effects) -> !effects.getEffects().isEmpty() && PotionBrewing.isBrewablePotion(effects)).toList();
            Potion potion = list.get(random.nextInt(list.size()));
            ItemStack itemstack1 = PotionUtils.setPotion(new ItemStack(this.toItem.getItem(), this.toCount), potion);
            return new MerchantOffer(itemstack, new ItemStack(this.fromItem, this.fromCount), itemstack1, this.maxUses, this.villagerXp, this.priceMultiplier);
        }
    }

    /**
     * The type Enchant book for emeralds.
     */
    @SuppressWarnings("deprecation")
    public static class EnchantBookForEmeralds implements VillagerTrades.ItemListing {
        private final int villagerXp;

        /**
         * Instantiates a new Enchant book for emeralds.
         *
         * @param villagerXp the villager xp
         */
        public EnchantBookForEmeralds(int villagerXp) {
            this.villagerXp = villagerXp;
        }

        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            List<Enchantment> list = BuiltInRegistries.ENCHANTMENT.stream().filter(Enchantment::isTradeable).toList();
            Enchantment enchantment = list.get(random.nextInt(list.size()));
            int i = Mth.nextInt(random, enchantment.getMinLevel(), enchantment.getMaxLevel());
            ItemStack itemstack = EnchantedBookItem.createForEnchantment(new EnchantmentInstance(enchantment, i));
            int j = 2 + random.nextInt(5 + i * 10) + 3 * i;
            if (enchantment.isTreasureOnly()) {
                j *= 2;
            }

            if (j > 64) {
                j = 64;
            }

            return new MerchantOffer(new ItemStack(Items.EMERALD, j), new ItemStack(Items.BOOK), itemstack, 12, this.villagerXp, 0.2F);
        }
    }

    /**
     * The type Treasure map for emeralds.
     */
    public static class TreasureMapForEmeralds implements VillagerTrades.ItemListing {
        private final int emeraldCost;
        private final TagKey<Structure> destination;
        private final String displayName;
        private final MapDecoration.Type destinationType;
        private final int maxUses;
        private final int villagerXp;

        /**
         * Instantiates a new Treasure map for emeralds.
         *
         * @param emeraldCost     the emerald cost
         * @param destination     the destination
         * @param displayName     the display name
         * @param destinationType the destination type
         * @param maxUses         the max uses
         * @param villagerXp      the villager xp
         */
        public TreasureMapForEmeralds(int emeraldCost, TagKey<Structure> destination, String displayName, MapDecoration.Type destinationType, int maxUses, int villagerXp) {
            this.emeraldCost = emeraldCost;
            this.destination = destination;
            this.displayName = displayName;
            this.destinationType = destinationType;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
        }

        @Nullable
        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            if (!(trader.level() instanceof ServerLevel serverlevel)) {
                return null;
            } else {
                BlockPos blockpos = serverlevel.findNearestMapStructure(this.destination, trader.blockPosition(), 100, true);
                if (blockpos != null) {
                    ItemStack itemstack = MapItem.create(serverlevel, blockpos.getX(), blockpos.getZ(), (byte)2, true, true);
                    MapItem.renderBiomePreviewMap(serverlevel, itemstack);
                    MapItemSavedData.addTargetDecoration(itemstack, blockpos, "+", this.destinationType);
                    itemstack.setHoverName(Component.translatable(this.displayName));
                    return new MerchantOffer(new ItemStack(Items.EMERALD, this.emeraldCost), new ItemStack(Items.COMPASS), itemstack, this.maxUses, this.villagerXp, 0.2F);
                } else {
                    return null;
                }
            }
        }
    }

    /**
     * The type Dyed armor for emeralds.
     */
    public static class DyedArmorForEmeralds implements VillagerTrades.ItemListing {
        private final Item item;
        private final int value;
        private final int maxUses;
        private final int villagerXp;

        /**
         * Instantiates a new Dyed armor for emeralds.
         *
         * @param item  the item
         * @param value the value
         */
        public DyedArmorForEmeralds(Item item, int value) {
            this(item, value, 12, 1);
        }

        /**
         * Instantiates a new Dyed armor for emeralds.
         *
         * @param item       the item
         * @param value      the value
         * @param maxUses    the max uses
         * @param villagerXp the villager xp
         */
        public DyedArmorForEmeralds(Item item, int value, int maxUses, int villagerXp) {
            this.item = item;
            this.value = value;
            this.maxUses = maxUses;
            this.villagerXp = villagerXp;
        }

        public MerchantOffer getOffer(@NotNull Entity trader, @NotNull RandomSource random) {
            ItemStack itemstack = new ItemStack(Items.EMERALD, this.value);
            ItemStack itemstack1 = new ItemStack(this.item);
            if (this.item instanceof DyeableArmorItem) {
                List<DyeItem> list = Lists.newArrayList();
                list.add(getRandomDye(random));
                if (random.nextFloat() > 0.7F) {
                    list.add(getRandomDye(random));
                }

                if (random.nextFloat() > 0.8F) {
                    list.add(getRandomDye(random));
                }

                itemstack1 = DyeableLeatherItem.dyeArmor(itemstack1, list);
            }

            return new MerchantOffer(itemstack, itemstack1, this.maxUses, this.villagerXp, 0.2F);
        }

        private static @NotNull DyeItem getRandomDye(@NotNull RandomSource random) {
            return DyeItem.byColor(DyeColor.byId(random.nextInt(16)));
        }
    }
}
