package com.tiedup.remake.network.trader;

import com.tiedup.remake.cells.CampOwnership;
import com.tiedup.remake.cells.CellDataV2;
import com.tiedup.remake.cells.CellRegistryV2;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.entities.EntitySlaveTrader;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.util.tasks.ItemTask;
import com.tiedup.remake.v2.BodyRegionV2;
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.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;

/**
 * Packet for buying a captive from a SlaveTrader.
 *
 * Client -> Server
 */
public class PacketBuyCaptive {

    private final int traderEntityId;
    private final UUID captiveId;

    public PacketBuyCaptive(int traderEntityId, UUID captiveId) {
        this.traderEntityId = traderEntityId;
        this.captiveId = captiveId;
    }

    public void encode(FriendlyByteBuf buf) {
        buf.writeInt(traderEntityId);
        buf.writeUUID(captiveId);
    }

    public static PacketBuyCaptive decode(FriendlyByteBuf buf) {
        int traderEntityId = buf.readInt();
        UUID captiveId = buf.readUUID();
        return new PacketBuyCaptive(traderEntityId, captiveId);
    }

    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);
    }

    private void handleServer(ServerPlayer buyer) {
        ServerLevel level = buyer.serverLevel();

        // Find trader entity
        Entity traderEntity = level.getEntity(traderEntityId);
        if (!(traderEntity instanceof EntitySlaveTrader trader)) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.trader_not_found").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        // Validate distance to trader
        if (buyer.distanceTo(trader) > 10.0) {
            return;
        }

        // Verify player has token
        if (!EntityKidnapper.hasTokenInInventory(buyer)) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.need_token").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        // Find captive in trader's camp cells
        UUID campId = trader.getCampUUID();
        if (campId == null) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.trader_no_camp").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        CampOwnership ownership = CampOwnership.get(level);
        CampOwnership.CampData campData = ownership.getCamp(campId);
        if (campData == null) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.camp_not_found").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        CellRegistryV2 cellRegistry = CellRegistryV2.get(level);
        List<CellDataV2> cells = cellRegistry.findCellsNear(
            campData.getCenter(),
            50.0
        );

        CellDataV2 targetCell = null;
        for (CellDataV2 cell : cells) {
            if (cell.hasPrisoner(captiveId)) {
                targetCell = cell;
                break;
            }
        }

        if (targetCell == null) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.captive_not_in_camp").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        // Find captive - try Player first, then Entity (Damsel)
        net.minecraft.world.entity.LivingEntity captiveEntity = null;
        IRestrainable kidnappedState = null;

        ServerPlayer captivePlayer = level
            .getServer()
            .getPlayerList()
            .getPlayer(captiveId);
        if (captivePlayer != null) {
            captiveEntity = captivePlayer;
            kidnappedState = KidnappedHelper.getKidnappedState(captivePlayer);
        } else {
            // Try as Entity (Damsel)
            net.minecraft.world.entity.Entity entity = level.getEntity(
                captiveId
            );
            if (
                entity instanceof net.minecraft.world.entity.LivingEntity living
            ) {
                captiveEntity = living;
                kidnappedState = KidnappedHelper.getKidnappedState(living);
            }
        }

        if (kidnappedState == null || !kidnappedState.isForSell()) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.captive_not_for_sale").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        // Get price
        ItemTask price = kidnappedState.getSalePrice();
        if (price == null) {
            buyer.sendSystemMessage(
                Component.translatable("msg.tiedup.network.price_not_set").withStyle(
                    ChatFormatting.RED
                )
            );
            return;
        }

        // Check if buyer has enough items
        int required = price.getAmount();
        int available = countItemInInventory(buyer, price);

        if (available < required) {
            buyer.sendSystemMessage(
                Component.translatable(
                    "msg.tiedup.network.not_enough_items",
                    required,
                    price.getItem().getDescription().getString(),
                    available
                ).withStyle(ChatFormatting.RED)
            );
            return;
        }

        // Take payment
        removeItemFromInventory(buyer, price, required);

        // Mark captive as sold immediately (prevents re-purchase)
        kidnappedState.cancelSale();

        // Order maid to fetch and deliver the captive
        com.tiedup.remake.entities.EntityMaid maid = trader.getMaid();
        if (maid != null && !maid.getMaidState().isBusy()) {
            // Maid will fetch from cell, release, and deliver to buyer
            maid.startFetchAndDeliver(captiveId, buyer.getUUID());

            TiedUpMod.LOGGER.info(
                "[PacketBuyCaptive] {} bought captive {} from trader {} for {} {} - maid delivering",
                buyer.getName().getString(),
                captiveId.toString().substring(0, 8),
                trader.getNpcName(),
                required,
                price.getItem().getDescription().getString()
            );

            buyer.sendSystemMessage(
                Component.translatable(
                    "msg.tiedup.network.purchase_maid_delivery"
                ).withStyle(ChatFormatting.GREEN)
            );
        } else {
            // No maid available - release directly (fallback)
            // Cancel sale state (already called above but defensive)
            kidnappedState.cancelSale();

            // Transfer collar ownership to buyer
            net.minecraft.world.item.ItemStack collar =
                kidnappedState.getEquipment(BodyRegionV2.NECK);
            if (
                !collar.isEmpty() &&
                com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar)
            ) {
                // Remove all existing owners from collar NBT
                for (UUID ownerId : new java.util.ArrayList<>(
                    com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar)
                )) {
                    com.tiedup.remake.v2.bondage.CollarHelper.removeOwner(collar, ownerId);
                }
                // Add buyer as new owner
                com.tiedup.remake.v2.bondage.CollarHelper.addOwner(
                    collar,
                    buyer.getUUID(),
                    buyer.getName().getString()
                );
                if (collar.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable) {
                    lockable.setLocked(collar, false);
                }

                // Re-apply modified collar to persist NBT changes
                kidnappedState.equip(BodyRegionV2.NECK, collar);

                // Update CollarRegistry for SlaveManagementScreen
                com.tiedup.remake.state.CollarRegistry collarRegistry =
                    com.tiedup.remake.state.CollarRegistry.get(level);
                if (collarRegistry != null) {
                    // Remove all previous owners from registry
                    collarRegistry.unregisterWearer(captiveId);
                    // Register buyer as new owner
                    collarRegistry.registerCollar(captiveId, buyer.getUUID());
                }

                // Sync collar changes to client
                if (captivePlayer != null) {
                    com.tiedup.remake.network.sync.SyncManager.syncBindState(
                        captivePlayer
                    );
                }
            }

            // Release via PrisonerService (state transition + cell cleanup + restraint removal)
            com.tiedup.remake.prison.service.EscapeMonitorService.get().release(
                level,
                captiveId,
                6000L // 5 minutes grace period
            );

            TiedUpMod.LOGGER.info(
                "[PacketBuyCaptive] {} bought captive {} from trader {} for {} {} - direct release (no maid)",
                buyer.getName().getString(),
                captiveId.toString().substring(0, 8),
                trader.getNpcName(),
                required,
                price.getItem().getDescription().getString()
            );

            buyer.sendSystemMessage(
                Component.translatable(
                    "tiedup.trader.purchase_success"
                ).withStyle(ChatFormatting.GREEN)
            );
        }
    }

    private int countItemInInventory(ServerPlayer player, ItemTask task) {
        if (task.getItem() == null) return 0;

        int count = 0;
        for (int i = 0; i < player.getInventory().getContainerSize(); i++) {
            ItemStack stack = player.getInventory().getItem(i);
            if (!stack.isEmpty() && stack.getItem() == task.getItem()) {
                count += stack.getCount();
            }
        }
        return count;
    }

    private void removeItemFromInventory(
        ServerPlayer player,
        ItemTask task,
        int amount
    ) {
        if (task.getItem() == null) return;

        int remaining = amount;
        for (
            int i = 0;
            i < player.getInventory().getContainerSize() && remaining > 0;
            i++
        ) {
            ItemStack stack = player.getInventory().getItem(i);
            if (!stack.isEmpty() && stack.getItem() == task.getItem()) {
                int toRemove = Math.min(stack.getCount(), remaining);
                stack.shrink(toRemove);
                remaining -= toRemove;
            }
        }
    }
}
