package com.tiedup.remake.entities.damsel.components;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.AbstractTiedUpNpc;
import com.tiedup.remake.entities.BondageServiceHandler;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.state.ICaptor;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.state.IRestrainableEntity;
import com.tiedup.remake.util.RestraintEffectUtils;
import com.tiedup.remake.util.tasks.ItemTask;
import com.tiedup.remake.util.teleport.Position;
import com.tiedup.remake.util.teleport.TeleportHelper;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

/**
 * DamselBondageManager - Facade over NpcEquipmentManager and NpcCaptivityManager.
 *
 * <p>H9 refactor: Split from a 1441-line god-component into:</p>
 * <ul>
 *   <li>{@link NpcEquipmentManager} -- equipment CRUD, coercion, bulk ops (~700L)</li>
 *   <li>{@link NpcCaptivityManager} -- captor, leash, free/capture (~250L)</li>
 *   <li>{@link BondageServiceHandler} -- existing, unchanged</li>
 *   <li>This facade -- delegates everything, owns sale state inline (~250L)</li>
 * </ul>
 *
 * <p>Implements {@link IRestrainable} so all existing callers via
 * {@code getBondageManager().xxx()} continue to work unchanged.</p>
 */
public class DamselBondageManager implements IRestrainable {

    // FIELDS

    private final AbstractTiedUpNpc entity;
    private final IBondageHost host;
    private final NpcEquipmentManager equipment;
    private final NpcCaptivityManager captivity;
    private final BondageServiceHandler bondageService;

    // Sale fields (inline -- too small for own class)
    private boolean forSale;
    private ItemTask salePrice;

    // CONSTRUCTOR

    public DamselBondageManager(AbstractTiedUpNpc entity, IBondageHost host) {
        this.entity = entity;
        this.host = host;
        this.equipment = new NpcEquipmentManager(entity, host);
        this.captivity = new NpcCaptivityManager(entity, host, equipment);
        this.bondageService = new BondageServiceHandler(entity);
        this.forSale = false;
        this.salePrice = null;
    }

    // SUB-COMPONENT ACCESS

    /** Expose equipment sub-component for direct access where needed. */
    public NpcEquipmentManager getEquipmentManager() {
        return equipment;
    }

    /** Expose captivity sub-component for direct access where needed. */
    public NpcCaptivityManager getCaptivityManager() {
        return captivity;
    }

    // IBondageState DELEGATION -> equipment

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

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

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

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

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

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

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

    @Override
    public boolean isBoundAndGagged() {
        return equipment.isBoundAndGagged();
    }

    @Override
    public boolean hasGaggingEffect() {
        return equipment.hasGaggingEffect();
    }

    @Override
    public boolean hasBlindingEffect() {
        return equipment.hasBlindingEffect();
    }

    @Override
    public boolean hasKnives() {
        return equipment.hasKnives();
    }

    @Override
    public boolean hasClothesWithSmallArms() {
        return equipment.hasClothesWithSmallArms();
    }

    @Override
    public boolean hasLockedCollar() {
        return equipment.hasLockedCollar();
    }

    @Override
    public boolean hasNamedCollar() {
        return equipment.hasNamedCollar();
    }

    // V2 region-based equipment access
    @Override
    public ItemStack getEquipment(BodyRegionV2 region) {
        return equipment.getInRegion(region);
    }

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

    // Equipment putters (local delegates — no longer in interface, but still called by legacy code)
    public void putBindOn(ItemStack bind) {
        equipment.putBindOn(bind);
    }

    public void putGagOn(ItemStack gag) {
        equipment.putGagOn(gag);
    }

    public void putBlindfoldOn(ItemStack blindfold) {
        equipment.putBlindfoldOn(blindfold);
    }

    public void putEarplugsOn(ItemStack earplugs) {
        equipment.putEarplugsOn(earplugs);
    }

    public void putCollarOn(ItemStack collar) {
        equipment.putCollarOn(collar);
    }

    public void putClothesOn(ItemStack clothes) {
        equipment.putClothesOn(clothes);
    }

    public void putMittensOn(ItemStack mittens) {
        equipment.putMittensOn(mittens);
    }

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

    @Override
    public ItemStack forceUnequip(BodyRegionV2 region) {
        // Delegate to NpcEquipmentManager.forceRemoveFromRegion which does the same
        // work as takeXOff (clear slot, sync, onUnequipped, side effects) but
        // bypasses ILockable.isLocked() checks. See RISK-001.
        return equipment.forceRemoveFromRegion(region);
    }

    // Legacy equipment removers (local delegates for internal use)
    public ItemStack takeBindOff() {
        return equipment.takeBindOff();
    }

    public ItemStack takeGagOff() {
        return equipment.takeGagOff();
    }

    public ItemStack takeBlindfoldOff() {
        return equipment.takeBlindfoldOff();
    }

    public ItemStack takeEarplugsOff() {
        return equipment.takeEarplugsOff();
    }

    public ItemStack takeCollarOff() {
        return equipment.takeCollarOff();
    }

    public ItemStack takeCollarOff(boolean force) {
        return equipment.takeCollarOff(force);
    }

    public ItemStack takeClothesOff() {
        return equipment.takeClothesOff();
    }

    public ItemStack takeMittensOff() {
        return equipment.takeMittensOff();
    }

    // Equipment replacer (V2 region-based)
    @Override
    public ItemStack replaceEquipment(
        BodyRegionV2 region,
        ItemStack newStack,
        boolean force
    ) {
        return switch (region) {
            case ARMS -> equipment.replaceBind(newStack, force);
            case MOUTH -> equipment.replaceGag(newStack, force);
            case EYES -> equipment.replaceBlindfold(newStack, force);
            case EARS -> equipment.replaceEarplugs(newStack, force);
            case NECK -> equipment.replaceCollar(newStack, force);
            case TORSO -> equipment.replaceClothes(newStack, force);
            case HANDS -> equipment.replaceMittens(newStack, force);
            default -> ItemStack.EMPTY;
        };
    }

    // Bulk operations
    @Override
    public void applyBondage(
        ItemStack bind,
        ItemStack gag,
        ItemStack blindfold,
        ItemStack earplugs,
        ItemStack collar,
        ItemStack clothes
    ) {
        equipment.applyBondage(bind, gag, blindfold, earplugs, collar, clothes);
    }

    @Override
    public void dropBondageItems(boolean drop) {
        equipment.dropBondageItems(drop);
    }

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

    @Override
    public void dropBondageItems(
        boolean drop,
        boolean dropBind,
        boolean dropGag,
        boolean dropBlindfold,
        boolean dropEarplugs,
        boolean dropCollar,
        boolean dropClothes
    ) {
        equipment.dropBondageItems(
            drop,
            dropBind,
            dropGag,
            dropBlindfold,
            dropEarplugs,
            dropCollar,
            dropClothes
        );
    }

    @Override
    public void dropClothes() {
        equipment.dropClothes();
    }

    @Override
    public int getBondageItemsWhichCanBeRemovedCount() {
        return equipment.getBondageItemsWhichCanBeRemovedCount();
    }

    @Override
    public boolean canBeTiedUp() {
        return equipment.canBeTiedUp();
    }

    // Permissions
    @Override
    public boolean canTakeOffClothes(Player player) {
        return equipment.canTakeOffClothes(player);
    }

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

    @Override
    public boolean canChangeClothes() {
        return equipment.canChangeClothes();
    }

    // ICoercible DELEGATION -> equipment
    @Override
    public void tighten(Player tightener) {
        equipment.tighten(tightener);
    }

    @Override
    public void applyChloroform(int duration) {
        equipment.applyChloroform(duration);
    }

    @Override
    public void shockKidnapped() {
        equipment.shockKidnapped();
    }

    @Override
    public void shockKidnapped(String messageAddon, float damage) {
        equipment.shockKidnapped(messageAddon, damage);
    }

    @Override
    public void takeBondageItemBy(IRestrainableEntity taker, int slotIndex) {
        equipment.takeBondageItemBy(taker, slotIndex);
    }

    // ICapturable DELEGATION -> captivity

    @Override
    public boolean isCaptive() {
        return captivity.isCaptive();
    }

    @Override
    public boolean isEnslavable() {
        return captivity.isEnslavable();
    }

    @Override
    public ICaptor getCaptor() {
        return captivity.getCaptor();
    }

    @Override
    public boolean getCapturedBy(ICaptor newCaptor) {
        return captivity.getCapturedBy(newCaptor);
    }

    @Override
    public void free() {
        captivity.free();
    }

    @Override
    public void free(boolean transportState) {
        captivity.free(transportState);
    }

    @Override
    public void transferCaptivityTo(ICaptor newCaptor) {
        captivity.transferCaptivityTo(newCaptor);
    }

    @Override
    public boolean isTiedToPole() {
        return captivity.isTiedToPole();
    }

    @Override
    public boolean tieToClosestPole(int searchRadius) {
        return captivity.tieToClosestPole(searchRadius);
    }

    @Override
    public boolean canBeKidnappedByEvents() {
        return captivity.canBeKidnappedByEvents();
    }

    @Override
    @Nullable
    public Entity getTransport() {
        return null;
    } // NPCs use vanilla leash directly

    // Public methods not on IRestrainable but used externally
    public void clearCaptor() {
        captivity.clearCaptor();
    }

    public boolean forceCapturedBy(ICaptor newCaptor) {
        return captivity.forceCapturedBy(newCaptor);
    }

    public void restoreCaptorFromUUID() {
        captivity.restoreCaptorFromUUID();
    }

    // Collar owner check (public, used by AbstractTiedUpNpc)
    public boolean isCollarOwner(Player player) {
        return equipment.isCollarOwner(player);
    }

    // ISaleable -- INLINE (4 methods, 2 fields)

    @Override
    public boolean isForSell() {
        return forSale && salePrice != null;
    }

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

    @Override
    public void putForSale(ItemTask price) {
        if (price == null) return;

        forSale = true;
        salePrice = price;

        TiedUpMod.LOGGER.info(
            "[EntityDamsel] {} put for sale at {}",
            host.getNpcName(),
            price.toDisplayString()
        );
    }

    @Override
    public void cancelSale() {
        if (!forSale) return;

        TiedUpMod.LOGGER.info(
            "[EntityDamsel] {} sale cancelled",
            host.getNpcName()
        );

        forSale = false;
        salePrice = null;
    }

    // IRestrainableEntity -- IDENTITY (stays in facade)

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

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

    @Override
    public String getKidnappedName() {
        return host.getNpcName();
    }

    @Override
    public String getNameFromCollar() {
        ItemStack collar = getEquipment(BodyRegionV2.NECK);
        if (collar.isEmpty()) {
            return host.getNpcName();
        }

        if (collar.hasCustomHoverName()) {
            return collar.getHoverName().getString();
        }

        return host.getNpcName();
    }

    @Override
    public void kidnappedDropItem(ItemStack stack) {
        host.dropItemStack(stack);
    }

    @Override
    public void teleportToPosition(Position position) {
        if (position == null || entity.level().isClientSide) return;
        TeleportHelper.teleportEntity(entity, position);
    }

    // CROSS-CUTTING METHODS (stay in facade)

    /**
     * Untie: crosses both equipment and captivity domains.
     * Drops items (if requested), clears equipment, removes speed reduction.
     */
    @Override
    public void untie(boolean drop) {
        if (drop) {
            equipment.dropBondageItems(true);
        } else {
            // Clear all V2 regions at once, then sync ONCE (batch)
            equipment.clearAllAndSync();
        }

        // Remove speed reduction
        RestraintEffectUtils.removeBindSpeedReduction(entity);

        TiedUpMod.LOGGER.info(
            "[EntityDamsel] {} freed from restraints",
            host.getNpcName()
        );
    }

    /**
     * Death handler: crosses captivity (free) and equipment (unlock).
     */
    @Override
    public boolean onDeathKidnapped(Level world) {
        if (world.isClientSide) return false;

        // Check if we have a collar with cell configured
        ItemStack collar = getEquipment(BodyRegionV2.NECK);
        if (collar.isEmpty()) return false;

        if (!CollarHelper.isCollar(collar)) return false;

        UUID cellId = CollarHelper.getCellId(collar);
        if (cellId == null) return false;

        // Get cell position from registry
        if (
            !(entity.level() instanceof
                    net.minecraft.server.level.ServerLevel serverLevel)
        ) return false;
        com.tiedup.remake.cells.CellDataV2 cell =
            com.tiedup.remake.cells.CellRegistryV2.get(serverLevel).getCell(
                cellId
            );
        if (cell == null) return false;

        Position cellPosition = new Position(
            cell.getSpawnPoint().above(),
            serverLevel.dimension()
        );

        // We have a cell -- respawn there instead of dying

        // 1. Free from captivity if captured
        if (isCaptive()) {
            captivity.free(false);
        }

        // 2. Unlock all locked items
        equipment.unlockAllItems();

        // 3. Heal to full
        host.setHealth(entity.getMaxHealth());

        // 4. Teleport to cell
        teleportToPosition(cellPosition);

        TiedUpMod.LOGGER.info(
            "[EntityDamsel] {} respawned at cell instead of dying",
            entity.getName().getString()
        );

        return true;
    }

    // BONDAGE SERVICE DELEGATION

    public boolean isBondageServiceEnabled() {
        return bondageService.isEnabled();
    }

    public String getBondageServiceMessage() {
        return bondageService.getMessage();
    }

    /**
     * Handle damage with bondage service logic.
     * Call this from EntityDamsel.hurt() if service is enabled.
     *
     * @param source damage source
     * @param amount damage amount
     * @return true if damage was handled (cancel default), false otherwise
     */
    public boolean handleDamageWithService(DamageSource source, float amount) {
        if (!isBondageServiceEnabled()) return false;

        // Only process player attacks
        if (!(source.getEntity() instanceof Player player)) return false;

        // Cancel actual damage (service NPCs don't take damage from hits)
        return true;
    }

    // PERSISTENCE ORCHESTRATION

    /**
     * Save bondage state to NBT.
     * Delegates to sub-components, handles sale state inline.
     */
    public void saveToTag(CompoundTag tag) {
        equipment.saveEquipmentToTag(tag);
        captivity.saveCaptivityToTag(tag);

        // Sale state (inline)
        tag.putBoolean("ForSale", forSale);
        if (salePrice != null) {
            tag.put("SalePrice", salePrice.save());
        }
    }

    /**
     * Load bondage state from NBT.
     * Delegates to sub-components, handles sale state inline.
     */
    public void loadFromTag(CompoundTag tag) {
        equipment.loadEquipmentFromTag(tag);
        captivity.loadCaptivityFromTag(tag);

        // Sale state (inline)
        forSale = tag.getBoolean("ForSale");
        if (tag.contains("SalePrice")) {
            salePrice = ItemTask.load(tag.getCompound("SalePrice"));
        }
    }
}
