package com.tiedup.remake.compat.mca.capability;

import com.tiedup.remake.compat.mca.MCABondageManager;
import com.tiedup.remake.compat.mca.MCACompat;
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
import com.tiedup.remake.v2.bondage.component.ComponentType;
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
import com.tiedup.remake.items.base.IHasResistance;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.state.ICaptor;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.state.IRestrainableEntity;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.util.tasks.ItemTask;
import com.tiedup.remake.util.teleport.Position;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

/**
 * Adapter that implements IRestrainable for MCA villagers.
 *
 * Wraps an MCA villager entity and delegates bondage state
 * to MCAKidnappedCapability. Uses MCABondageManager for
 * lifecycle events and sync operations.
 */
public class MCAKidnappedAdapter implements IRestrainable {

    private final LivingEntity entity;
    private final MCAKidnappedCapability cap;

    public MCAKidnappedAdapter(
        LivingEntity entity,
        MCAKidnappedCapability cap
    ) {
        this.entity = entity;
        this.cap = cap;
    }

    // 1. CAPTURE LIFECYCLE

    @Override
    public boolean getCapturedBy(ICaptor captor) {
        if (!isEnslavable()) {
            return false;
        }

        // Notify captor (CRITICAL: this updates captor's state so it stops hunting)
        captor.addCaptive(this);

        // Set captor UUID
        Entity captorEntity = captor.getEntity();
        if (captorEntity != null) {
            cap.setCaptorUUID(captorEntity.getUUID());
        }

        // For MCA villagers (which are Mobs), use vanilla leash
        if (entity instanceof Mob mob && captorEntity != null) {
            mob.setLeashedTo(captorEntity, true);
        }

        // Notify manager
        MCABondageManager.getInstance().onVillagerCaptured(
            entity,
            captorEntity
        );

        return true;
    }

    @Override
    public void free() {
        free(true);
    }

    @Override
    public void free(boolean transportState) {
        // Notify captor first, before clearing UUID
        ICaptor captor = getCaptor();
        if (captor != null) {
            captor.removeCaptive(this, transportState);
        }

        cap.setCaptorUUID(null);

        // Remove vanilla leash
        if (entity instanceof Mob mob) {
            mob.dropLeash(true, transportState);
        }

        // Notify manager
        MCABondageManager.getInstance().onVillagerFreed(entity);
    }

    @Override
    public void transferCaptivityTo(ICaptor newCaptor) {
        ICaptor currentCaptor = getCaptor();
        if (currentCaptor != null && !currentCaptor.allowCaptiveTransfer()) {
            return;
        }

        free(false);
        getCapturedBy(newCaptor);
    }

    // 2. STATE QUERIES - CAPTURE

    @Override
    public boolean isEnslavable() {
        // Can be captured if tied up OR has collar
        return isTiedUp() || hasCollar();
    }

    @Override
    public boolean isCaptive() {
        // Check if entity is leashed (vanilla leash for Mobs)
        if (entity instanceof Mob mob) {
            return mob.isLeashed();
        }
        return cap.isCaptured();
    }

    @Override
    public boolean canBeTiedUp() {
        return !isTiedUp();
    }

    @Override
    public boolean isTiedToPole() {
        // MCA villagers can be tied to poles via vanilla leash to fence
        if (entity instanceof Mob mob && mob.isLeashed()) {
            Entity holder = mob.getLeashHolder();
            return holder != null && !(holder instanceof LivingEntity);
        }
        return false;
    }

    @Override
    public boolean tieToClosestPole(int searchRadius) {
        // MCA villagers use vanilla leash mechanics
        // This would need to find a fence and attach leash
        // For now, return false (not implemented for MCA)
        return false;
    }

    @Override
    public boolean isForSell() {
        return cap.isForSale();
    }

    @Override
    @Nullable
    public ItemTask getSalePrice() {
        return cap.getSalePrice();
    }

    @Override
    public void putForSale(ItemTask price) {
        cap.setForSale(true);
        cap.setSalePrice(price);
    }

    @Override
    public void cancelSale() {
        cap.setForSale(false);
        cap.setSalePrice(null);
    }

    @Override
    public boolean canBeKidnappedByEvents() {
        // MCA villagers can be kidnapped by events
        return true;
    }

    @Override
    @Nullable
    public ICaptor getCaptor() {
        UUID captorUUID = cap.getCaptorUUID();
        if (captorUUID == null) {
            return null;
        }

        // Try to find captor entity in world
        if (entity.level() instanceof ServerLevel serverLevel) {
            Entity captorEntity = serverLevel.getEntity(captorUUID);
            if (captorEntity instanceof ICaptor kidnapper) {
                return kidnapper;
            }
            // Players need special handling via PlayerCaptorManager
            if (captorEntity instanceof Player player) {
                return PlayerBindState.getInstance(player).getCaptorManager();
            }
        }
        return null;
    }

    @Override
    @Nullable
    public Entity getTransport() {
        // MCA villagers use vanilla leash directly, no proxy entity
        return null;
    }

    // 3. STATE QUERIES - BONDAGE EQUIPMENT

    @Override
    public boolean isTiedUp() {
        return cap.hasBind();
    }

    @Override
    public boolean isGagged() {
        return cap.hasGag();
    }

    @Override
    public boolean isBlindfolded() {
        return cap.hasBlindfold();
    }

    @Override
    public boolean hasEarplugs() {
        return cap.hasEarplugs();
    }

    @Override
    public boolean hasCollar() {
        return cap.hasCollar();
    }

    @Override
    public boolean hasLockedCollar() {
        ItemStack collar = cap.getCollar();
        return (
            !collar.isEmpty() &&
            collar.getItem() instanceof ILockable lockable &&
            lockable.isLocked(collar)
        );
    }

    @Override
    public boolean hasNamedCollar() {
        ItemStack collar = cap.getCollar();
        return !collar.isEmpty() && collar.hasCustomHoverName();
    }

    @Override
    public boolean hasClothes() {
        return cap.hasClothes();
    }

    @Override
    public boolean hasMittens() {
        return cap.hasMittens();
    }

    @Override
    public boolean hasClothesWithSmallArms() {
        // MCA villagers use their own model, not relevant here
        return false;
    }

    @Override
    public boolean isBoundAndGagged() {
        return isTiedUp() && isGagged();
    }

    @Override
    public boolean hasGaggingEffect() {
        ItemStack gag = cap.getGag();
        if (gag.isEmpty()) return false;
        return DataDrivenBondageItem.getComponent(gag, ComponentType.GAGGING, GaggingComponent.class) != null;
    }

    @Override
    public boolean hasBlindingEffect() {
        ItemStack blindfold = cap.getBlindfold();
        if (blindfold.isEmpty()) return false;
        return DataDrivenBondageItem.getComponent(blindfold, ComponentType.BLINDING, BlindingComponent.class) != null;
    }

    @Override
    public boolean hasKnives() {
        // MCA villagers don't have knife inventory by default
        return false;
    }

    // 4. EQUIPMENT MANAGEMENT - PUT ON

    public void putBindOn(ItemStack bind) {
        cap.setBind(bind);

        // Set initial resistance from item
        if (bind.getItem() instanceof IHasResistance resistance) {
            cap.setBindResistance(resistance.getBaseResistance(entity));
        }

        // Notify manager (handles AI changes)
        MCABondageManager.getInstance().onVillagerTied(entity, bind);

        checkBindAfterApply();
    }

    public void putGagOn(ItemStack gag) {
        cap.setGag(gag);
        MCABondageManager.getInstance().onSensoryRestrictionChanged(entity);
        checkGagAfterApply();
    }

    public void putBlindfoldOn(ItemStack blindfold) {
        cap.setBlindfold(blindfold);
        MCABondageManager.getInstance().onSensoryRestrictionChanged(entity);
        checkBlindfoldAfterApply();
    }

    public void putEarplugsOn(ItemStack earplugs) {
        cap.setEarplugs(earplugs);
        checkEarplugsAfterApply();
    }

    public void putCollarOn(ItemStack collar) {
        cap.setCollar(collar);

        if (collar.getItem() instanceof IHasResistance resistance) {
            cap.setCollarResistance(resistance.getBaseResistance(entity));
        }

        MCABondageManager.getInstance().onCollarChanged(entity, true);
        checkCollarAfterApply();
    }

    public void putClothesOn(ItemStack clothes) {
        cap.setClothes(clothes);
        syncToClients();
    }

    public void putMittensOn(ItemStack mittens) {
        cap.setMittens(mittens);
        checkMittensAfterApply();
    }

    // 5. EQUIPMENT MANAGEMENT - UNEQUIP (V2)

    @Override
    public ItemStack unequip(BodyRegionV2 region) {
        return switch (region) {
            case ARMS -> takeBindOff();
            case MOUTH -> takeGagOff();
            case EYES -> takeBlindfoldOff();
            case EARS -> takeEarplugsOff();
            case NECK -> takeCollarOff(false);
            case TORSO -> takeClothesOff();
            case HANDS -> takeMittensOff();
            default -> ItemStack.EMPTY;
        };
    }

    @Override
    public ItemStack forceUnequip(BodyRegionV2 region) {
        // Force-remove: same logic as takeXOff but bypasses isLocked checks.
        // NECK and TORSO already have correct handling. See RISK-001.
        return switch (region) {
            case NECK -> takeCollarOff(true);
            case TORSO -> takeClothesOff();
            case ARMS -> {
                ItemStack current = cap.getBind();
                if (current.isEmpty()) yield ItemStack.EMPTY;
                cap.setBind(ItemStack.EMPTY);
                cap.setBindResistance(0);
                MCABondageManager.getInstance().onVillagerUntied(entity);
                syncToClients();
                yield current;
            }
            case MOUTH -> {
                ItemStack current = cap.getGag();
                if (current.isEmpty()) yield ItemStack.EMPTY;
                cap.setGag(ItemStack.EMPTY);
                MCABondageManager.getInstance().onSensoryRestrictionChanged(
                    entity
                );
                syncToClients();
                yield current;
            }
            case EYES -> {
                ItemStack current = cap.getBlindfold();
                if (current.isEmpty()) yield ItemStack.EMPTY;
                cap.setBlindfold(ItemStack.EMPTY);
                MCABondageManager.getInstance().onSensoryRestrictionChanged(
                    entity
                );
                syncToClients();
                yield current;
            }
            case EARS -> {
                ItemStack current = cap.getEarplugs();
                if (current.isEmpty()) yield ItemStack.EMPTY;
                cap.setEarplugs(ItemStack.EMPTY);
                syncToClients();
                yield current;
            }
            case HANDS -> {
                ItemStack current = cap.getMittens();
                if (current.isEmpty()) yield ItemStack.EMPTY;
                cap.setMittens(ItemStack.EMPTY);
                syncToClients();
                yield current;
            }
            default -> ItemStack.EMPTY;
        };
    }

    // 5b. EQUIPMENT MANAGEMENT - TAKE OFF (local helpers)

    public ItemStack takeBindOff() {
        ItemStack current = cap.getBind();
        if (isLocked(current, false)) {
            return ItemStack.EMPTY;
        }
        cap.setBind(ItemStack.EMPTY);
        cap.setBindResistance(0);

        // Notify manager (handles AI changes)
        MCABondageManager.getInstance().onVillagerUntied(entity);

        syncToClients();
        return current;
    }

    public ItemStack takeGagOff() {
        ItemStack current = cap.getGag();
        if (isLocked(current, false)) {
            return ItemStack.EMPTY;
        }
        cap.setGag(ItemStack.EMPTY);
        MCABondageManager.getInstance().onSensoryRestrictionChanged(entity);
        syncToClients();
        return current;
    }

    public ItemStack takeBlindfoldOff() {
        ItemStack current = cap.getBlindfold();
        if (isLocked(current, false)) {
            return ItemStack.EMPTY;
        }
        cap.setBlindfold(ItemStack.EMPTY);
        MCABondageManager.getInstance().onSensoryRestrictionChanged(entity);
        syncToClients();
        return current;
    }

    public ItemStack takeEarplugsOff() {
        ItemStack current = cap.getEarplugs();
        if (isLocked(current, false)) {
            return ItemStack.EMPTY;
        }
        cap.setEarplugs(ItemStack.EMPTY);
        syncToClients();
        return current;
    }

    public ItemStack takeCollarOff() {
        return takeCollarOff(false);
    }

    public ItemStack takeCollarOff(boolean force) {
        ItemStack current = cap.getCollar();
        if (!force && isLocked(current, false)) {
            return ItemStack.EMPTY;
        }
        cap.setCollar(ItemStack.EMPTY);
        cap.setCollarResistance(0);
        MCABondageManager.getInstance().onCollarChanged(entity, false);
        syncToClients();
        return current;
    }

    public ItemStack takeClothesOff() {
        ItemStack current = cap.getClothes();
        cap.setClothes(ItemStack.EMPTY);
        syncToClients();
        return current;
    }

    public ItemStack takeMittensOff() {
        ItemStack current = cap.getMittens();
        if (isLocked(current, false)) {
            return ItemStack.EMPTY;
        }
        cap.setMittens(ItemStack.EMPTY);
        syncToClients();
        return current;
    }

    // V2 Region-Based Equipment Access

    @Override
    public ItemStack getEquipment(BodyRegionV2 region) {
        return switch (region) {
            case ARMS -> cap.getBind();
            case MOUTH -> cap.getGag();
            case EYES -> cap.getBlindfold();
            case EARS -> cap.getEarplugs();
            case NECK -> cap.getCollar();
            case TORSO -> cap.getClothes();
            case HANDS -> cap.getMittens();
            default -> ItemStack.EMPTY;
        };
    }

    @Override
    public void equip(BodyRegionV2 region, ItemStack stack) {
        switch (region) {
            case ARMS -> putBindOn(stack);
            case MOUTH -> putGagOn(stack);
            case EYES -> putBlindfoldOn(stack);
            case EARS -> putEarplugsOn(stack);
            case NECK -> putCollarOn(stack);
            case TORSO -> putClothesOn(stack);
            case HANDS -> putMittensOn(stack);
            default -> {
            }
        }
    }

    // 7. EQUIPMENT MANAGEMENT - REPLACE (V2 region-based)

    @Override
    public ItemStack replaceEquipment(
        BodyRegionV2 region,
        ItemStack newStack,
        boolean force
    ) {
        return switch (region) {
            case ARMS -> {
                ItemStack old = cap.getBind();
                if (!force && isLocked(old, false)) yield ItemStack.EMPTY;
                cap.setBind(newStack);
                if (newStack.getItem() instanceof IHasResistance resistance) {
                    cap.setBindResistance(resistance.getBaseResistance(entity));
                }
                yield old;
            }
            case MOUTH -> {
                ItemStack old = cap.getGag();
                if (!force && isLocked(old, false)) yield ItemStack.EMPTY;
                cap.setGag(newStack);
                yield old;
            }
            case EYES -> {
                ItemStack old = cap.getBlindfold();
                if (!force && isLocked(old, false)) yield ItemStack.EMPTY;
                cap.setBlindfold(newStack);
                yield old;
            }
            case EARS -> {
                ItemStack old = cap.getEarplugs();
                if (!force && isLocked(old, false)) yield ItemStack.EMPTY;
                cap.setEarplugs(newStack);
                yield old;
            }
            case NECK -> {
                ItemStack old = cap.getCollar();
                if (!force && isLocked(old, false)) yield ItemStack.EMPTY;
                cap.setCollar(newStack);
                if (newStack.getItem() instanceof IHasResistance resistance) {
                    cap.setCollarResistance(
                        resistance.getBaseResistance(entity)
                    );
                }
                yield old;
            }
            case TORSO -> {
                ItemStack old = cap.getClothes();
                cap.setClothes(newStack);
                yield old;
            }
            case HANDS -> {
                ItemStack old = cap.getMittens();
                if (!force && isLocked(old, false)) yield ItemStack.EMPTY;
                cap.setMittens(newStack);
                yield old;
            }
            default -> ItemStack.EMPTY;
        };
    }

    // 8. BULK OPERATIONS

    @Override
    public void applyBondage(
        ItemStack bind,
        ItemStack gag,
        ItemStack blindfold,
        ItemStack earplugs,
        ItemStack collar,
        ItemStack clothes
    ) {
        if (!bind.isEmpty()) putBindOn(bind);
        if (!gag.isEmpty()) putGagOn(gag);
        if (!blindfold.isEmpty()) putBlindfoldOn(blindfold);
        if (!earplugs.isEmpty()) putEarplugsOn(earplugs);
        if (!collar.isEmpty()) putCollarOn(collar);
        if (!clothes.isEmpty()) putClothesOn(clothes);
    }

    @Override
    public void untie(boolean drop) {
        dropBondageItems(drop);
        free();
    }

    @Override
    public void dropBondageItems(boolean drop) {
        dropBondageItems(drop, true, true, true, true, true, true);
    }

    @Override
    public void dropBondageItems(boolean drop, boolean dropBind) {
        dropBondageItems(drop, dropBind, true, true, true, true, true);
    }

    @Override
    public void dropBondageItems(
        boolean drop,
        boolean dropBind,
        boolean dropGag,
        boolean dropBlindfold,
        boolean dropEarplugs,
        boolean dropCollar,
        boolean dropClothes
    ) {
        if (!drop) return;

        if (dropBind && !isLocked(cap.getBind(), false)) {
            kidnappedDropItem(takeBindOff());
        }
        if (dropGag && !isLocked(cap.getGag(), false)) {
            kidnappedDropItem(takeGagOff());
        }
        if (dropBlindfold && !isLocked(cap.getBlindfold(), false)) {
            kidnappedDropItem(takeBlindfoldOff());
        }
        if (dropEarplugs && !isLocked(cap.getEarplugs(), false)) {
            kidnappedDropItem(takeEarplugsOff());
        }
        if (dropCollar && !isLocked(cap.getCollar(), false)) {
            kidnappedDropItem(takeCollarOff());
        }
        if (dropClothes) {
            kidnappedDropItem(takeClothesOff());
        }
    }

    @Override
    public void dropClothes() {
        kidnappedDropItem(takeClothesOff());
    }

    @Override
    public int getBondageItemsWhichCanBeRemovedCount() {
        int count = 0;
        if (
            !cap.getBind().isEmpty() && !isLocked(cap.getBind(), false)
        ) count++;
        if (!cap.getGag().isEmpty() && !isLocked(cap.getGag(), false)) count++;
        if (
            !cap.getBlindfold().isEmpty() &&
            !isLocked(cap.getBlindfold(), false)
        ) count++;
        if (
            !cap.getEarplugs().isEmpty() && !isLocked(cap.getEarplugs(), false)
        ) count++;
        if (
            !cap.getCollar().isEmpty() && !isLocked(cap.getCollar(), false)
        ) count++;
        if (!cap.getClothes().isEmpty()) count++;
        return count;
    }

    // 9. CLOTHES PERMISSION SYSTEM

    @Override
    public boolean canTakeOffClothes(Player player) {
        // MCA villagers allow anyone to remove clothes
        return true;
    }

    @Override
    public boolean canChangeClothes(Player player) {
        return true;
    }

    @Override
    public boolean canChangeClothes() {
        return true;
    }

    // 10. SPECIAL INTERACTIONS

    @Override
    public void tighten(Player tightener) {
        // Reset bind resistance to maximum
        ItemStack bind = cap.getBind();
        if (
            !bind.isEmpty() &&
            bind.getItem() instanceof IHasResistance resistance
        ) {
            cap.setBindResistance(resistance.getBaseResistance(entity));
        }
    }

    @Override
    public void applyChloroform(int duration) {
        // Apply slowness and weakness effects
        entity.addEffect(
            new net.minecraft.world.effect.MobEffectInstance(
                net.minecraft.world.effect.MobEffects.MOVEMENT_SLOWDOWN,
                duration,
                4 // Strong slowness
            )
        );
        entity.addEffect(
            new net.minecraft.world.effect.MobEffectInstance(
                net.minecraft.world.effect.MobEffects.WEAKNESS,
                duration,
                1
            )
        );
    }

    @Override
    public void shockKidnapped() {
        shockKidnapped("", 1.0F);
    }

    @Override
    public void shockKidnapped(String messageAddon, float damage) {
        entity.hurt(entity.damageSources().magic(), damage);
        // Could play shock sound here
    }

    @Override
    public void takeBondageItemBy(IRestrainableEntity taker, int slotIndex) {
        ItemStack taken = switch (slotIndex) {
            case 0 -> takeBindOff();
            case 1 -> takeGagOff();
            case 2 -> takeBlindfoldOff();
            case 3 -> takeEarplugsOff();
            case 4 -> takeCollarOff();
            case 5 -> takeClothesOff();
            default -> ItemStack.EMPTY;
        };

        if (!taken.isEmpty()) {
            taker.kidnappedDropItem(taken);
        }
    }

    // 11. POST-APPLY CALLBACKS

    /**
     * Sync bondage state to tracking clients.
     * Delegates to MCABondageManager.
     */
    private void syncToClients() {
        MCABondageManager.getInstance().syncBondageState(entity);
    }

    @Override
    public void checkBindAfterApply() {
        syncToClients();
    }

    @Override
    public void checkGagAfterApply() {
        syncToClients();
    }

    @Override
    public void checkBlindfoldAfterApply() {
        syncToClients();
    }

    @Override
    public void checkEarplugsAfterApply() {
        syncToClients();
    }

    @Override
    public void checkCollarAfterApply() {
        syncToClients();
    }

    @Override
    public void checkMittensAfterApply() {
        syncToClients();
    }

    // 12. DEATH & LIFECYCLE

    @Override
    public boolean onDeathKidnapped(Level world) {
        // Drop all bondage items on death
        dropBondageItems(true);
        free();

        // Cleanup manager state
        MCABondageManager.getInstance().removeVillager(entity);

        return true;
    }

    // 13. UTILITY & METADATA

    @Override
    public UUID getKidnappedUniqueId() {
        return entity.getUUID();
    }

    @Override
    public String getKidnappedName() {
        return entity.getName().getString();
    }

    @Override
    public String getNameFromCollar() {
        ItemStack collar = cap.getCollar();
        if (!collar.isEmpty() && collar.hasCustomHoverName()) {
            return collar.getHoverName().getString();
        }
        return getKidnappedName();
    }

    @Override
    public void kidnappedDropItem(ItemStack stack) {
        if (!stack.isEmpty() && !entity.level().isClientSide) {
            ItemEntity itemEntity = new ItemEntity(
                entity.level(),
                entity.getX(),
                entity.getY() + 0.5,
                entity.getZ(),
                stack
            );
            itemEntity.setDefaultPickUpDelay();
            entity.level().addFreshEntity(itemEntity);
        }
    }

    @Override
    public void teleportToPosition(Position position) {
        if (
            entity instanceof
                net.minecraft.server.level.ServerPlayer serverPlayer
        ) {
            serverPlayer.teleportTo(
                position.getX(),
                position.getY(),
                position.getZ()
            );
        } else if (entity.level() instanceof ServerLevel) {
            entity.teleportTo(
                position.getX(),
                position.getY(),
                position.getZ()
            );
        }
    }

    // 14.5. RESISTANCE SYSTEM

    @Override
    public int getCurrentBindResistance() {
        return cap.getBindResistance();
    }

    @Override
    public void setCurrentBindResistance(int resistance) {
        cap.setBindResistance(resistance);
    }

    @Override
    public int getCurrentCollarResistance() {
        return cap.getCollarResistance();
    }

    @Override
    public void setCurrentCollarResistance(int resistance) {
        cap.setCollarResistance(resistance);
    }

    // 15. ENTITY REFERENCE

    @Override
    public LivingEntity asLivingEntity() {
        return entity;
    }
}
