package com.tiedup.remake.util;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.network.sync.SyncManager;
import com.tiedup.remake.state.IBondageState;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Helper class for common bondage equipment interaction logic.
 *
 * This class extracts the common interactLivingEntity pattern
 * for bondage equipment items (gags, blindfolds, mittens, earplugs, collars).
 */
public class EquipmentInteractionHelper {

    /**
     * Standard equipment interaction for bondage items.
     *
     * Handles the common flow: check state → equip or replace equipment.
     *
     * @param stack The item stack being used
     * @param player The player using the item
     * @param target The entity being equipped
     * @param isEquipped Predicate to check if target already has this equipment
     * @param putOn Consumer to equip the item on target (state, itemCopy)
     * @param replace Function to replace existing equipment (state, itemCopy) → oldItem
     * @param sendMessage Consumer to send message to target (player, target)
     * @param logPrefix Log prefix for this equipment type
     * @return InteractionResult
     */
    public static InteractionResult equipOnTarget(
        ItemStack stack,
        Player player,
        LivingEntity target,
        Predicate<IBondageState> isEquipped,
        BiConsumer<IBondageState, ItemStack> putOn,
        BiFunction<IBondageState, ItemStack, ItemStack> replace,
        BiConsumer<Player, LivingEntity> sendMessage,
        String logPrefix
    ) {
        return equipOnTarget(
            stack,
            player,
            target,
            isEquipped,
            putOn,
            replace,
            sendMessage,
            logPrefix,
            null,
            null,
            null
        );
    }

    /**
     * Equipment interaction with additional hooks for special items (like Collar).
     *
     * @param stack The item stack being used
     * @param player The player using the item
     * @param target The entity being equipped
     * @param isEquipped Predicate to check if target already has this equipment
     * @param putOn Consumer to equip the item on target (state, itemCopy)
     * @param replace Function to replace existing equipment (state, itemCopy) → oldItem
     * @param sendMessage Consumer to send message to target (player, target)
     * @param logPrefix Log prefix for this equipment type
     * @param preEquipHook Optional hook called before equipping (for owner registration, etc.)
     * @param postEquipHook Optional hook called after equipping (for sound, particles, etc.)
     * @param canReplace Optional predicate to check if replacement is allowed (for lock check)
     * @return InteractionResult
     */
    public static InteractionResult equipOnTarget(
        ItemStack stack,
        Player player,
        LivingEntity target,
        Predicate<IBondageState> isEquipped,
        BiConsumer<IBondageState, ItemStack> putOn,
        BiFunction<IBondageState, ItemStack, ItemStack> replace,
        BiConsumer<Player, LivingEntity> sendMessage,
        String logPrefix,
        @Nullable EquipContext preEquipHook,
        @Nullable EquipContext postEquipHook,
        @Nullable ReplacePredicate canReplace
    ) {
        // Server-side only
        if (player.level().isClientSide) {
            return InteractionResult.SUCCESS;
        }

        // Get target state
        IBondageState targetState = KidnappedHelper.getKidnappedState(target);
        if (targetState == null) {
            return InteractionResult.PASS;
        }

        // Must be tied up
        if (!targetState.isTiedUp()) {
            return InteractionResult.PASS;
        }

        // Pre-equip hook (e.g., register collar owner)
        if (preEquipHook != null) {
            preEquipHook.execute(stack, player, target, targetState);
        }

        // Case 1: Not equipped - put on new
        if (!isEquipped.test(targetState)) {
            putOn.accept(targetState, stack.copy());
            stack.shrink(1);

            sendMessage.accept(player, target);
            syncIfPlayer(target);

            // Post-equip hook (e.g., play sound)
            if (postEquipHook != null) {
                postEquipHook.execute(stack, player, target, targetState);
            }

            TiedUpMod.LOGGER.info(
                "[{}] {} put {} on {}",
                logPrefix,
                player.getName().getString(),
                logPrefix.toLowerCase(),
                target.getName().getString()
            );

            return InteractionResult.SUCCESS;
        }
        // Case 2: Already equipped - replace
        else {
            // Check if replacement is allowed (e.g., not locked)
            if (
                canReplace != null &&
                !canReplace.canReplace(targetState, player)
            ) {
                return InteractionResult.PASS;
            }

            ItemStack oldItem = replace.apply(targetState, stack.copy());
            if (oldItem != null && !oldItem.isEmpty()) {
                stack.shrink(1);
                targetState.kidnappedDropItem(oldItem);

                sendMessage.accept(player, target);
                syncIfPlayer(target);

                // Post-equip hook
                if (postEquipHook != null) {
                    postEquipHook.execute(stack, player, target, targetState);
                }

                TiedUpMod.LOGGER.info(
                    "[{}] {} replaced {} on {}",
                    logPrefix,
                    player.getName().getString(),
                    logPrefix.toLowerCase(),
                    target.getName().getString()
                );

                return InteractionResult.SUCCESS;
            }
        }

        return InteractionResult.PASS;
    }

    /**
     * Sync inventory if target is a ServerPlayer.
     */
    private static void syncIfPlayer(LivingEntity target) {
        if (target instanceof ServerPlayer serverPlayer) {
            SyncManager.syncInventory(serverPlayer);
        }
    }

    /**
     * Functional interface for pre/post equip hooks.
     */
    @FunctionalInterface
    public interface EquipContext {
        void execute(
            ItemStack stack,
            Player player,
            LivingEntity target,
            IBondageState state
        );
    }

    /**
     * Functional interface to check if replacement is allowed.
     */
    @FunctionalInterface
    public interface ReplacePredicate {
        boolean canReplace(IBondageState state, Player player);
    }
}
