package com.tiedup.remake.dispenser;

import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.v2.bondage.CollarHelper;
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.v2.bondage.datadriven.DataDrivenItemDefinition;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import net.minecraft.world.item.ItemStack;

/**
 * Generic dispenser behavior for equipping bondage items.
 * Uses V2 data-driven item detection instead of V1 class checks.
 *
 * Use factory methods to create instances for each bondage type.
 */
public class GenericBondageDispenseBehavior
    extends EquipBondageDispenseBehavior
{

    private final Predicate<ItemStack> itemCheck;
    private final Predicate<IBondageState> canEquipCheck;
    private final BiConsumer<IBondageState, ItemStack> equipAction;

    private GenericBondageDispenseBehavior(
        Predicate<ItemStack> itemCheck,
        Predicate<IBondageState> canEquipCheck,
        BiConsumer<IBondageState, ItemStack> equipAction
    ) {
        this.itemCheck = itemCheck;
        this.canEquipCheck = canEquipCheck;
        this.equipAction = equipAction;
    }

    @Override
    protected boolean isValidItem(ItemStack stack) {
        return !stack.isEmpty() && itemCheck.test(stack);
    }

    @Override
    protected boolean canEquip(IBondageState state) {
        return state != null && canEquipCheck.test(state);
    }

    @Override
    protected void equip(IBondageState state, ItemStack stack) {
        if (state != null) {
            equipAction.accept(state, stack);
        }
    }

    // Factory Methods

    /** Universal behavior for the V2 data-driven item singleton. Dispatches by region. */
    public static GenericBondageDispenseBehavior forAnyDataDriven() {
        return new GenericBondageDispenseBehavior(
            stack -> DataDrivenItemRegistry.get(stack) != null,
            state -> true, // let equip() handle the check
            (state, stack) -> {
                DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
                if (def == null) return;
                java.util.Set<BodyRegionV2> regions = def.occupiedRegions();
                if (regions.contains(BodyRegionV2.ARMS) && !state.isTiedUp()) {
                    state.equip(BodyRegionV2.ARMS, stack);
                } else if (regions.contains(BodyRegionV2.MOUTH) && !state.isGagged()) {
                    state.equip(BodyRegionV2.MOUTH, stack);
                } else if (regions.contains(BodyRegionV2.EYES) && !state.isBlindfolded()) {
                    state.equip(BodyRegionV2.EYES, stack);
                } else if (regions.contains(BodyRegionV2.NECK) && !state.hasCollar()) {
                    state.equip(BodyRegionV2.NECK, stack);
                } else if (regions.contains(BodyRegionV2.EARS) && !state.hasEarplugs()) {
                    state.equip(BodyRegionV2.EARS, stack);
                } else if (regions.contains(BodyRegionV2.HANDS) && !state.hasMittens()) {
                    state.equip(BodyRegionV2.HANDS, stack);
                }
            }
        );
    }

    public static GenericBondageDispenseBehavior forBind() {
        return new GenericBondageDispenseBehavior(
            BindModeHelper::isBindItem,
            state -> !state.isTiedUp(),
            (s, i) -> s.equip(BodyRegionV2.ARMS, i)
        );
    }

    public static GenericBondageDispenseBehavior forGag() {
        return new GenericBondageDispenseBehavior(
            stack -> DataDrivenBondageItem.getComponent(stack, ComponentType.GAGGING, GaggingComponent.class) != null,
            state -> !state.isGagged(),
            (s, i) -> s.equip(BodyRegionV2.MOUTH, i)
        );
    }

    public static GenericBondageDispenseBehavior forBlindfold() {
        return new GenericBondageDispenseBehavior(
            stack -> {
                DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
                return def != null && def.occupiedRegions().contains(BodyRegionV2.EYES);
            },
            state -> !state.isBlindfolded(),
            (s, i) -> s.equip(BodyRegionV2.EYES, i)
        );
    }

    public static GenericBondageDispenseBehavior forCollar() {
        return new GenericBondageDispenseBehavior(
            CollarHelper::isCollar,
            state -> !state.hasCollar(),
            (s, i) -> s.equip(BodyRegionV2.NECK, i)
        );
    }

    public static GenericBondageDispenseBehavior forEarplugs() {
        return new GenericBondageDispenseBehavior(
            stack -> {
                DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
                return def != null && def.occupiedRegions().contains(BodyRegionV2.EARS);
            },
            state -> !state.hasEarplugs(),
            (s, i) -> s.equip(BodyRegionV2.EARS, i)
        );
    }
}
