package com.tiedup.remake.network.merchant;

import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityKidnapperMerchant;
import com.tiedup.remake.entities.MerchantTrade;
import java.util.List;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.ChatFormatting;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraftforge.network.NetworkEvent;

/**
 * Packet sent from client to server when player attempts to purchase a trade.
 *
 * Contains:
 * - Merchant entity ID
 * - Trade index (which trade to purchase)
 */
public class PacketPurchaseTrade {

    private final UUID merchantUUID;
    private final int tradeIndex;

    /**
     * Create packet to purchase a trade.
     *
     * @param merchantUUID The UUID of the merchant entity (persistent across restarts)
     * @param tradeIndex Index of the trade to purchase (0-based)
     */
    public PacketPurchaseTrade(UUID merchantUUID, int tradeIndex) {
        this.merchantUUID = merchantUUID;
        this.tradeIndex = tradeIndex;
    }

    /**
     * Encode the packet to the network buffer.
     */
    public void encode(FriendlyByteBuf buf) {
        buf.writeUUID(merchantUUID);
        buf.writeInt(tradeIndex);
    }

    /**
     * Decode the packet from the network buffer.
     */
    public static PacketPurchaseTrade decode(FriendlyByteBuf buf) {
        UUID uuid = buf.readUUID();
        int tradeIndex = buf.readInt();
        return new PacketPurchaseTrade(uuid, tradeIndex);
    }

    /**
     * Handle the packet on the receiving side (SERVER SIDE).
     */
    public void handle(Supplier<NetworkEvent.Context> ctx) {
        ctx
            .get()
            .enqueueWork(() -> {
                ServerPlayer player = ctx.get().getSender();
                if (player == null) {
                    return;
                }

                // CRITICAL FIX: Add rate limiting to prevent DoS via packet spam
                if (
                    !com.tiedup.remake.network.PacketRateLimiter.allowPacket(
                        player,
                        "action"
                    )
                ) {
                    return;
                }

                handleServer(player);
            });
        ctx.get().setPacketHandled(true);
    }

    /**
     * Handle the packet on the server side.
     * Validates and executes the trade.
     */
    private void handleServer(ServerPlayer player) {
        // Find the merchant entity by UUID (persistent across restarts)
        Entity entity = (
            (net.minecraft.server.level.ServerLevel) player.level()
        ).getEntity(merchantUUID);
        if (!(entity instanceof EntityKidnapperMerchant merchant)) {
            TiedUpMod.LOGGER.warn(
                "[PacketPurchaseTrade] Entity {} is not a merchant or not found",
                merchantUUID
            );
            return;
        }

        // Validate merchant is in range (10 blocks)
        if (player.distanceTo(merchant) > 10.0) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "Merchant is too far away!"
            );
            return;
        }

        // Validate merchant is in merchant mode
        if (!merchant.isMerchant()) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "The merchant is too angry to trade!"
            );
            return;
        }

        // Get the trade
        List<MerchantTrade> trades = merchant.getTrades();
        if (tradeIndex < 0 || tradeIndex >= trades.size()) {
            TiedUpMod.LOGGER.warn(
                "[PacketPurchaseTrade] Invalid trade index: {}",
                tradeIndex
            );
            return;
        }

        MerchantTrade trade = trades.get(tradeIndex);

        // Count total gold in player inventory (across all slots)
        int totalIngots = countGoldIngots(player);
        int totalNuggets = countGoldNuggets(player);

        // Check if player can afford
        if (
            totalIngots < trade.getIngotPrice() ||
            totalNuggets < trade.getNuggetPrice()
        ) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "Not enough gold!"
            );
            return;
        }

        // Consume payment from multiple slots if needed
        consumeGoldIngots(player, trade.getIngotPrice());
        consumeGoldNuggets(player, trade.getNuggetPrice());

        // Give item to player
        ItemStack purchasedItem = trade.getItem().copy();
        if (!player.getInventory().add(purchasedItem)) {
            // Inventory full, drop at feet
            player.drop(purchasedItem, false);
        }

        // Success message
        SystemMessageManager.sendChatToPlayer(
            player,
            "Purchased: " + trade.getItemName().getString(),
            ChatFormatting.GREEN
        );

        // Merchant response
        merchant.talkToPlayersInRadius("Pleasure doing business!", 10);

        TiedUpMod.LOGGER.info(
            "[PacketPurchaseTrade] {} purchased {} from merchant",
            player.getName().getString(),
            trade.getItemName().getString()
        );
    }

    /**
     * Count total gold ingots across all inventory slots.
     */
    private int countGoldIngots(ServerPlayer player) {
        int total = 0;
        for (ItemStack stack : player.getInventory().items) {
            if (stack.is(Items.GOLD_INGOT)) {
                total += stack.getCount();
            }
        }
        return total;
    }

    /**
     * Count total gold nuggets across all inventory slots.
     */
    private int countGoldNuggets(ServerPlayer player) {
        int total = 0;
        for (ItemStack stack : player.getInventory().items) {
            if (stack.is(Items.GOLD_NUGGET)) {
                total += stack.getCount();
            }
        }
        return total;
    }

    /**
     * Consume gold ingots from inventory (multiple slots if needed).
     */
    private void consumeGoldIngots(ServerPlayer player, int amount) {
        int remaining = amount;
        for (ItemStack stack : player.getInventory().items) {
            if (remaining <= 0) break;
            if (stack.is(Items.GOLD_INGOT)) {
                int toRemove = Math.min(remaining, stack.getCount());
                stack.shrink(toRemove);
                remaining -= toRemove;
            }
        }
    }

    /**
     * Consume gold nuggets from inventory (multiple slots if needed).
     */
    private void consumeGoldNuggets(ServerPlayer player, int amount) {
        int remaining = amount;
        for (ItemStack stack : player.getInventory().items) {
            if (remaining <= 0) break;
            if (stack.is(Items.GOLD_NUGGET)) {
                int toRemove = Math.min(remaining, stack.getCount());
                stack.shrink(toRemove);
                remaining -= toRemove;
            }
        }
    }
}
