package com.tiedup.remake.util;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.state.IPlayerLeashAccess;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.FenceBlock;
import net.minecraft.world.level.block.WallBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;

/**
 *
 * Manages attribute modifiers for movement speed reduction and other effects.
 */
public class RestraintEffectUtils {

    // UUID for the movement speed modifier (must be consistent)
    private static final UUID BIND_SPEED_MODIFIER_UUID = UUID.fromString(
        "7f3c7c8e-9d4e-4c7a-8e5f-1a2b3c4d5e6f"
    );
    private static final String BIND_SPEED_MODIFIER_NAME = "tiedup.bind_speed";

    // Speed reduction: -0.09 (90% reduction) when tied up
    // Player base speed is 0.10, so this reduces them to 0.01 (10% speed)
    private static final double BIND_SPEED_REDUCTION = -0.09;

    // Full immobilization: -0.10 (100% reduction) for WRAP/LATEX_SACK
    // Player can only move by jumping
    private static final double FULL_IMMOBILIZATION_REDUCTION = -0.10;

    private static final boolean DEBUG = false;

    /**
     * Apply movement speed reduction to a tied entity.
     *
     * @param entity The living entity to apply the effect to
     * @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
     *             which handles speed via tick-based resolution. This method remains for NPC entities
     *             (Damsel, MCA villagers) that are not managed by MovementStyleManager.
     *             Scheduled for removal once NPC movement is migrated to V2.
     */
    @Deprecated(forRemoval = true)
    public static void applyBindSpeedReduction(LivingEntity entity) {
        applyBindSpeedReduction(entity, false);
    }

    /**
     * Apply movement speed reduction to a tied entity.
     *
     * @param entity The living entity to apply the effect to
     * @param fullImmobilization If true, applies 100% speed reduction (for WRAP/LATEX_SACK)
     * @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
     *             which handles speed via tick-based resolution. This method remains for NPC entities
     *             (Damsel, MCA villagers) that are not managed by MovementStyleManager.
     *             Scheduled for removal once NPC movement is migrated to V2.
     */
    @Deprecated(forRemoval = true)
    public static void applyBindSpeedReduction(
        LivingEntity entity,
        boolean fullImmobilization
    ) {
        if (entity == null) {
            TiedUpMod.LOGGER.warn(
                "[RESTRAINT-UTIL] Cannot apply speed reduction - entity is null"
            );
            return;
        }

        AttributeInstance movementSpeed = entity.getAttribute(
            Attributes.MOVEMENT_SPEED
        );
        if (movementSpeed == null) {
            TiedUpMod.LOGGER.error(
                "[RESTRAINT-UTIL] Entity {} has no MOVEMENT_SPEED attribute!",
                entity.getName().getString()
            );
            return;
        }

        // Remove existing modifier if present (to avoid duplicates)
        removeBindSpeedReduction(entity);

        // Choose reduction amount based on immobilization type
        double reduction = fullImmobilization
            ? FULL_IMMOBILIZATION_REDUCTION
            : BIND_SPEED_REDUCTION;

        // Create and apply new modifier
        AttributeModifier modifier = new AttributeModifier(
            BIND_SPEED_MODIFIER_UUID,
            BIND_SPEED_MODIFIER_NAME,
            reduction,
            AttributeModifier.Operation.ADDITION
        );

        movementSpeed.addPermanentModifier(modifier);

        if (DEBUG) {
            TiedUpMod.LOGGER.info(
                "[RESTRAINT-UTIL] Applied speed reduction to {} (base: {}, modified: {}, full: {})",
                entity.getName().getString(),
                movementSpeed.getBaseValue(),
                movementSpeed.getValue(),
                fullImmobilization
            );
        }
    }

    /**
     * Remove movement speed reduction from an entity.
     *
     * @param entity The living entity to remove the effect from
     * @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
     *             which handles speed cleanup via tick-based resolution. This method remains for
     *             NPC entities (Damsel, MCA villagers) that are not managed by MovementStyleManager.
     *             Scheduled for removal once NPC movement is migrated to V2.
     */
    @Deprecated(forRemoval = true)
    public static void removeBindSpeedReduction(LivingEntity entity) {
        if (entity == null) {
            TiedUpMod.LOGGER.warn(
                "[RESTRAINT-UTIL] Cannot remove speed reduction - entity is null"
            );
            return;
        }

        AttributeInstance movementSpeed = entity.getAttribute(
            Attributes.MOVEMENT_SPEED
        );
        if (movementSpeed == null) {
            TiedUpMod.LOGGER.error(
                "[RESTRAINT-UTIL] Entity {} has no MOVEMENT_SPEED attribute!",
                entity.getName().getString()
            );
            return;
        }

        // Remove modifier if present
        if (movementSpeed.getModifier(BIND_SPEED_MODIFIER_UUID) != null) {
            movementSpeed.removeModifier(BIND_SPEED_MODIFIER_UUID);

            if (DEBUG) {
                TiedUpMod.LOGGER.info(
                    "[RESTRAINT-UTIL] Removed speed reduction from {} (restored speed: {})",
                    entity.getName().getString(),
                    movementSpeed.getValue()
                );
            }
        } else {
            if (DEBUG) {
                TiedUpMod.LOGGER.debug(
                    "[RESTRAINT-UTIL] No speed modifier found on {} (already removed or never applied)",
                    entity.getName().getString()
                );
            }
        }
    }

    /**
     * Check if an entity currently has the bind speed reduction applied.
     *
     * @param entity The living entity to check
     * @return true if the modifier is active
     * @deprecated For players, movement style is tracked by {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}.
     *             Scheduled for removal once NPC movement is migrated to V2.
     */
    @Deprecated(forRemoval = true)
    public static boolean hasBindSpeedReduction(LivingEntity entity) {
        if (entity == null) return false;

        AttributeInstance movementSpeed = entity.getAttribute(
            Attributes.MOVEMENT_SPEED
        );
        if (movementSpeed == null) return false;

        return movementSpeed.getModifier(BIND_SPEED_MODIFIER_UUID) != null;
    }

    /**
     * Re-apply speed reduction if needed (called on login/respawn).
     *
     * @param entity The living entity
     * @param shouldBeSlowed Whether the entity should have reduced speed
     * @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
     *             which re-resolves on every tick. Scheduled for removal once NPC movement is migrated to V2.
     */
    @Deprecated(forRemoval = true)
    public static void updateBindSpeedReduction(
        LivingEntity entity,
        boolean shouldBeSlowed
    ) {
        updateBindSpeedReduction(entity, shouldBeSlowed, false);
    }

    /**
     * Re-apply speed reduction if needed (called on login/respawn).
     *
     * @param entity The living entity
     * @param shouldBeSlowed Whether the entity should have reduced speed
     * @param fullImmobilization If true, applies 100% speed reduction (for WRAP/LATEX_SACK)
     * @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
     *             which re-resolves on every tick. Scheduled for removal once NPC movement is migrated to V2.
     */
    @Deprecated(forRemoval = true)
    public static void updateBindSpeedReduction(
        LivingEntity entity,
        boolean shouldBeSlowed,
        boolean fullImmobilization
    ) {
        boolean currentlySlowed = hasBindSpeedReduction(entity);

        if (shouldBeSlowed && !currentlySlowed) {
            // Need to apply
            applyBindSpeedReduction(entity, fullImmobilization);
            if (DEBUG) {
                TiedUpMod.LOGGER.info(
                    "[RESTRAINT-UTIL] Re-applied speed reduction to {} on login/respawn (full={})",
                    entity.getName().getString(),
                    fullImmobilization
                );
            }
        } else if (shouldBeSlowed && currentlySlowed) {
            // Already slowed - but might need to change immobilization level
            // Re-apply with correct level
            applyBindSpeedReduction(entity, fullImmobilization);
        } else if (!shouldBeSlowed && currentlySlowed) {
            // Need to remove
            removeBindSpeedReduction(entity);
            if (DEBUG) {
                TiedUpMod.LOGGER.info(
                    "[RESTRAINT-UTIL] Removed speed reduction from {} on login/respawn",
                    entity.getName().getString()
                );
            }
        }
    }

    // POLE BINDING UTILITIES

    /**
     * Find the closest fence or wall block within a radius.
     *
     * @param level The level to search in
     * @param center The center position to search from
     * @param radius The search radius in blocks
     * @return The position of the closest fence/wall, or null if none found
     */
    @Nullable
    public static BlockPos findClosestFence(
        Level level,
        BlockPos center,
        int radius
    ) {
        if (level == null || center == null) return null;

        BlockPos closestFence = null;
        double closestDistance = Double.MAX_VALUE;

        for (int x = -radius; x <= radius; x++) {
            for (int y = -radius; y <= radius; y++) {
                for (int z = -radius; z <= radius; z++) {
                    BlockPos checkPos = center.offset(x, y, z);
                    BlockState state = level.getBlockState(checkPos);

                    if (
                        state.getBlock() instanceof FenceBlock ||
                        state.getBlock() instanceof WallBlock
                    ) {
                        double dist = center.distSqr(checkPos);
                        if (dist < closestDistance) {
                            closestDistance = dist;
                            closestFence = checkPos;
                        }
                    }
                }
            }
        }

        return closestFence;
    }

    /**
     * Tie an entity to the closest fence or wall block.
     * Works for both Players (via LeashProxy) and NPCs (via vanilla leash).
     *
     * @param entity The entity to tie
     * @param searchRadius The search radius for fence blocks
     * @return true if successfully tied, false otherwise
     */
    public static boolean tieToClosestPole(
        LivingEntity entity,
        int searchRadius
    ) {
        if (entity == null || entity.level().isClientSide) return false;
        if (!(entity.level() instanceof ServerLevel serverLevel)) return false;

        BlockPos entityPos = entity.blockPosition();
        BlockPos closestFence = findClosestFence(
            serverLevel,
            entityPos,
            searchRadius
        );

        if (closestFence == null) {
            if (DEBUG) {
                TiedUpMod.LOGGER.debug(
                    "[RESTRAINT-UTIL] No fence found within {} blocks of {}",
                    searchRadius,
                    entity.getName().getString()
                );
            }
            return false;
        }

        // Get or create a LeashFenceKnotEntity at the fence position
        LeashFenceKnotEntity fenceKnot = LeashFenceKnotEntity.getOrCreateKnot(
            serverLevel,
            closestFence
        );

        if (fenceKnot == null) {
            TiedUpMod.LOGGER.warn(
                "[RESTRAINT-UTIL] Failed to create fence knot at {}",
                closestFence
            );
            return false;
        }

        // Handle differently based on entity type
        if (entity instanceof Player player) {
            // Player: use LeashProxy system
            if (player instanceof IPlayerLeashAccess access) {
                access.tiedup$attachLeash(fenceKnot);
                TiedUpMod.LOGGER.debug(
                    "[RESTRAINT-UTIL] Tied player {} to pole at {}",
                    player.getName().getString(),
                    closestFence
                );
                return true;
            } else {
                TiedUpMod.LOGGER.error(
                    "[RESTRAINT-UTIL] Player {} does not implement IPlayerLeashAccess!",
                    player.getName().getString()
                );
                return false;
            }
        } else if (entity instanceof Mob mob) {
            // NPC (Mob): use vanilla leash mechanics
            mob.setLeashedTo(fenceKnot, true);
            TiedUpMod.LOGGER.debug(
                "[RESTRAINT-UTIL] Tied mob {} to pole at {}",
                mob.getName().getString(),
                closestFence
            );
            return true;
        }

        return false;
    }

    // CHLOROFORM UTILITIES

    /**
     * Apply chloroform effects to an entity.
     * Effects: Slowness, Mining Fatigue, Blindness, Jump Boost (all at max amplifier).
     *
     * @param entity The entity to affect
     * @param durationSeconds Duration in seconds
     */
    public static void applyChloroformEffects(
        LivingEntity entity,
        int durationSeconds
    ) {
        if (entity == null || entity.level().isClientSide) return;

        int tickDuration = durationSeconds * GameConstants.TICKS_PER_SECOND;

        entity.addEffect(
            new MobEffectInstance(
                MobEffects.MOVEMENT_SLOWDOWN,
                tickDuration,
                GameConstants.CHLOROFORM_SLOWDOWN_AMPLIFIER,
                false,
                false
            )
        );
        entity.addEffect(
            new MobEffectInstance(
                MobEffects.DIG_SLOWDOWN,
                tickDuration,
                GameConstants.CHLOROFORM_DIG_SLOWDOWN_AMPLIFIER,
                false,
                false
            )
        );
        entity.addEffect(
            new MobEffectInstance(
                MobEffects.BLINDNESS,
                tickDuration,
                GameConstants.CHLOROFORM_BLINDNESS_AMPLIFIER,
                false,
                false
            )
        );
        entity.addEffect(
            new MobEffectInstance(
                MobEffects.JUMP,
                tickDuration,
                GameConstants.CHLOROFORM_JUMP_AMPLIFIER,
                false,
                false
            )
        );

        // Stop navigation for mobs
        if (entity instanceof Mob mob) {
            mob.getNavigation().stop();
        }

        if (DEBUG) {
            TiedUpMod.LOGGER.debug(
                "[RESTRAINT-UTIL] Applied chloroform to {} for {} seconds",
                entity.getName().getString(),
                durationSeconds
            );
        }
    }
}
