package com.tiedup.remake.v2.bondage.capability;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.network.ModNetwork;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
import com.tiedup.remake.v2.bondage.IV2BondageItem;
import com.tiedup.remake.v2.bondage.IV2EquipmentHolder;
import com.tiedup.remake.v2.bondage.V2EquipResult;
import com.tiedup.remake.v2.bondage.V2EquipmentManager;
import com.tiedup.remake.v2.bondage.network.PacketSyncV2Equipment;
import java.util.Map;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Static API for V2 bondage equipment operations.
 *
 * READ methods work on any side. WRITE methods are server-only and return
 * early if called on the client. This prevents desync and ensures the server
 * is the authority for equipment state.
 *
 * Currently dispatches only for Players (via Forge capability).
 */
public final class V2EquipmentHelper {

    private V2EquipmentHelper() {}

    // ==================== READ ====================

    /**
     * Get the V2 equipment capability for an entity.
     * @return The capability, or null if the entity doesn't support V2 equipment.
     */
    @Nullable
    public static IV2BondageEquipment getEquipment(LivingEntity entity) {
        if (entity == null) return null;
        if (entity instanceof Player player) {
            return player
                .getCapability(V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT)
                .orElse(null);
        }
        // V2 equipment holders (EntityDamsel, etc.)
        if (entity instanceof IV2EquipmentHolder holder) {
            return holder.getV2Equipment();
        }
        return null;
    }

    /**
     * Get the item in a specific region for an entity.
     * @return The ItemStack, or {@link ItemStack#EMPTY} if empty or entity unsupported.
     */
    public static ItemStack getInRegion(
        LivingEntity entity,
        BodyRegionV2 region
    ) {
        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return ItemStack.EMPTY;
        return equip.getInRegion(region);
    }

    /**
     * Check if a region is directly occupied on the given entity.
     */
    public static boolean isRegionOccupied(
        LivingEntity entity,
        BodyRegionV2 region
    ) {
        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return false;
        return equip.isRegionOccupied(region);
    }

    /**
     * Check if a region is blocked by any equipped item's {@link IV2BondageItem#getBlockedRegions()}.
     */
    public static boolean isRegionBlocked(
        LivingEntity entity,
        BodyRegionV2 region
    ) {
        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return false;
        return equip.isRegionBlocked(region);
    }

    /**
     * Get all equipped items (de-duplicated) for an entity.
     * @return Unmodifiable map, or empty map if entity unsupported.
     */
    public static Map<BodyRegionV2, ItemStack> getAllEquipped(
        LivingEntity entity
    ) {
        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return Map.of();
        return equip.getAllEquipped();
    }

    /**
     * Check if the entity has any V2 equipment at all.
     */
    public static boolean hasAnyEquipment(LivingEntity entity) {
        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return false;
        return equip.getEquippedCount() > 0;
    }

    // ==================== WRITE (server-only) ====================

    /**
     * Equip a V2 bondage item onto an entity.
     *
     * Validates the item implements {@link IV2BondageItem}, copies the stack,
     * runs conflict resolution via {@link V2EquipmentManager#tryEquip},
     * and fires lifecycle hooks.
     *
     * @param entity The target entity (must be server-side)
     * @param stack  The ItemStack to equip (must implement IV2BondageItem)
     * @return The equip result, or {@link V2EquipResult#BLOCKED} if invalid
     */
    public static V2EquipResult equipItem(
        LivingEntity entity,
        ItemStack stack
    ) {
        if (entity.level().isClientSide) return V2EquipResult.BLOCKED;
        if (stack == null || stack.isEmpty()) return V2EquipResult.BLOCKED;
        if (
            !(stack.getItem() instanceof IV2BondageItem item)
        ) return V2EquipResult.BLOCKED;

        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return V2EquipResult.BLOCKED;

        if (!item.canEquip(stack, entity)) return V2EquipResult.BLOCKED;

        // Copy the stack so the original isn't mutated
        ItemStack equipCopy = stack.copy();
        V2EquipResult result = V2EquipmentManager.tryEquip(
            equip,
            item,
            equipCopy,
            entity
        );

        if (result.isSuccess()) {
            item.onEquipped(equipCopy, entity);
            sync(entity);
        }

        return result;
    }

    /**
     * Unequip the item from a region, respecting canUnequip.
     *
     * @param entity The target entity (must be server-side)
     * @param region The region to unequip from
     * @return The removed ItemStack, or {@link ItemStack#EMPTY} if nothing removed
     */
    public static ItemStack unequipFromRegion(
        LivingEntity entity,
        BodyRegionV2 region
    ) {
        return unequipFromRegion(entity, region, false);
    }

    /**
     * Unequip the item from a region, optionally forcing removal.
     *
     * @param entity The target entity (must be server-side)
     * @param region The region to unequip from
     * @param force  If true, bypass canUnequip check
     * @return The removed ItemStack, or {@link ItemStack#EMPTY} if nothing removed
     */
    public static ItemStack unequipFromRegion(
        LivingEntity entity,
        BodyRegionV2 region,
        boolean force
    ) {
        if (entity.level().isClientSide) return ItemStack.EMPTY;

        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return ItemStack.EMPTY;

        ItemStack stack = equip.getInRegion(region);
        if (stack.isEmpty()) return ItemStack.EMPTY;

        if (!force && stack.getItem() instanceof IV2BondageItem item) {
            if (!item.canUnequip(stack, entity)) {
                return ItemStack.EMPTY;
            }
        }

        // Full scan to remove all region references to this stack (identity-based)
        V2EquipmentManager.removeAllRegionsOf(equip, stack);

        if (stack.getItem() instanceof IV2BondageItem item) {
            item.onUnequipped(stack, entity);
        }

        sync(entity);
        return stack;
    }

    /**
     * Clear all V2 equipment from an entity, firing onUnequipped for each.
     *
     * @param entity The target entity (must be server-side)
     */
    public static void clearAll(LivingEntity entity) {
        if (entity.level().isClientSide) return;

        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return;

        // Fire lifecycle hooks for each unique item before clearing
        Map<BodyRegionV2, ItemStack> equipped = equip.getAllEquipped();
        for (Map.Entry<BodyRegionV2, ItemStack> entry : equipped.entrySet()) {
            ItemStack stack = entry.getValue();
            if (stack.getItem() instanceof IV2BondageItem item) {
                item.onUnequipped(stack, entity);
            }
        }

        equip.clearAll();
        sync(entity);
    }

    // ==================== SYNC ====================

    /**
     * Sync V2 equipment state to tracking clients.
     * Sends the full serialized capability to the player and all trackers.
     */
    public static void sync(LivingEntity entity) {
        if (entity.level().isClientSide) return;

        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return;

        // IV2EquipmentHolder entities (Damsels) sync via EntityDataAccessor,
        // not via packet. Trigger their EntityData sync instead.
        if (entity instanceof IV2EquipmentHolder holder) {
            holder.syncEquipmentToData();
            return;
        }

        PacketSyncV2Equipment packet = new PacketSyncV2Equipment(
            entity.getId(),
            equip.serializeNBT()
        );

        if (entity instanceof ServerPlayer serverPlayer) {
            ModNetwork.sendToAllTrackingAndSelf(packet, serverPlayer);
        } else {
            ModNetwork.sendToAllTrackingEntity(packet, entity);
        }
    }

    /**
     * Sync V2 equipment to a specific player (used on login/start-tracking).
     */
    public static void syncTo(LivingEntity entity, ServerPlayer target) {
        // IV2EquipmentHolder entities sync via SynchedEntityData, not packets
        if (entity instanceof IV2EquipmentHolder holder) {
            holder.syncEquipmentToData();
            return;
        }

        IV2BondageEquipment equip = getEquipment(entity);
        if (equip == null) return;

        PacketSyncV2Equipment packet = new PacketSyncV2Equipment(
            entity.getId(),
            equip.serializeNBT()
        );
        ModNetwork.sendToPlayer(packet, target);
    }
}
