package com.tiedup.remake.state.components;

import com.tiedup.remake.core.ModSounds;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.ItemGpsCollar;
import com.tiedup.remake.items.ItemShockCollarAuto;
import com.tiedup.remake.state.hosts.IPlayerBindStateHost;
import com.tiedup.remake.util.GameConstants;
import com.tiedup.remake.util.time.Timer;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.List;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Component responsible for shock collar mechanics and GPS tracking.
 *
 * Single Responsibility: Collar automation and GPS monitoring
 * Complexity: HIGH (synchronized timer, GPS zone checks, network messages)
 * Risk: HIGH (timer thread management)
 *
 * Threading: Uses synchronized blocks for timer access (volatile Timer field)
 */
public class PlayerShockCollar {

    private final IPlayerBindStateHost host;

    // volatile: accessed from synchronized blocks across multiple threads
    private volatile Timer timerAutoShockCollar;
    private final Object lockTimerAutoShock = new Object();

    public PlayerShockCollar(IPlayerBindStateHost host) {
        this.host = host;
    }

    // ========== Shock Functionality ==========

    /**
     * Triggers a shock with default damage.
     */
    public void shockKidnapped() {
        this.shockKidnapped(null, GameConstants.DEFAULT_SHOCK_DAMAGE);
    }

    /**
     * Triggers a visual and auditory shock effect.
     * Damage is applied (shock can kill).
     *
     * @param messageAddon Optional message addon for HUD (e.g., GPS violation)
     * @param damage Shock damage amount
     */
    public void shockKidnapped(@Nullable String messageAddon, float damage) {
        Player player = host.getPlayer();
        if (player == null || !player.isAlive()) return;

        // Sound effects
        player
            .level()
            .playSound(
                null,
                player.blockPosition(),
                ModSounds.ELECTRIC_SHOCK.get(),
                net.minecraft.sounds.SoundSource.PLAYERS,
                GameConstants.SHOCK_SOUND_VOLUME,
                GameConstants.SHOCK_SOUND_PITCH
            );

        // Particle effects
        if (
            player.level() instanceof
                net.minecraft.server.level.ServerLevel serverLevel
        ) {
            serverLevel.sendParticles(
                net.minecraft.core.particles.ParticleTypes.ELECTRIC_SPARK,
                player.getX(),
                player.getY() + GameConstants.SHOCK_PARTICLE_Y_OFFSET,
                player.getZ(),
                GameConstants.SHOCK_PARTICLE_COUNT,
                GameConstants.SHOCK_PARTICLE_X_SPREAD,
                GameConstants.SHOCK_PARTICLE_Y_SPREAD,
                GameConstants.SHOCK_PARTICLE_Z_SPREAD,
                GameConstants.SHOCK_PARTICLE_SPEED
            );
        }

        // Damage logic - shock can kill
        if (damage > 0) {
            player.hurt(player.damageSources().magic(), damage);
        }

        // HUD Message via SystemMessageManager
        if (messageAddon != null) {
            // Custom message with addon (e.g., GPS violation)
            SystemMessageManager.sendToPlayer(
                player,
                MessageCategory.SLAVE_SHOCK,
                SystemMessageManager.getTemplate(MessageCategory.SLAVE_SHOCK) +
                    messageAddon
            );
        } else {
            SystemMessageManager.sendToPlayer(
                player,
                MessageCategory.SLAVE_SHOCK
            );
        }
    }

    // ========== Auto-Shock & GPS Monitoring ==========

    /**
     * Periodic check for Auto-Shock intervals and GPS Safe Zones.
     * Called from RestraintTaskTickHandler every player tick.
     *
     * Thread Safety: Refactored to avoid alien method calls under lock.
     * We compute shock decisions inside synchronized block, then execute
     * outside the lock to prevent deadlock risk.
     */
    public void checkAutoShockCollar() {
        Player player = host.getPlayer();
        if (player == null || !player.isAlive()) return;

        // Flags set inside lock, actions performed outside
        boolean shouldShockAuto = false;
        boolean shouldShockGPS = false;
        ItemGpsCollar gpsCollar = null;
        ItemStack gpsStack = null;

        synchronized (lockTimerAutoShock) {
            ItemStack collarStack = getCurrentCollar();
            if (collarStack.isEmpty()) return;

            // Auto-Shock Collar handling
            if (
                collarStack.getItem() instanceof ItemShockCollarAuto collarShock
            ) {
                if (
                    timerAutoShockCollar != null &&
                    timerAutoShockCollar.isExpired()
                ) {
                    shouldShockAuto = true;
                }

                if (
                    timerAutoShockCollar == null ||
                    timerAutoShockCollar.isExpired()
                ) {
                    timerAutoShockCollar = new Timer(
                        collarShock.getInterval() /
                            GameConstants.TICKS_PER_SECOND,
                        player.level()
                    );
                }
            }
            // GPS Collar handling
            else if (collarStack.getItem() instanceof ItemGpsCollar gps) {
                if (
                    gps.isActive(collarStack) &&
                    (timerAutoShockCollar == null ||
                        timerAutoShockCollar.isExpired())
                ) {
                    List<ItemGpsCollar.SafeSpot> safeSpots = gps.getSafeSpots(
                        collarStack
                    );
                    if (safeSpots != null && !safeSpots.isEmpty()) {
                        boolean isSafe = false;
                        for (ItemGpsCollar.SafeSpot spot : safeSpots) {
                            if (spot.isInside(player)) {
                                isSafe = true;
                                break;
                            }
                        }
                        if (!isSafe) {
                            timerAutoShockCollar = new Timer(
                                gps.getShockInterval(collarStack) /
                                    GameConstants.TICKS_PER_SECOND,
                                player.level()
                            );
                            shouldShockGPS = true;
                            gpsCollar = gps;
                            gpsStack = collarStack.copy();
                        }
                    }
                }
            }
        }

        // Execute shock actions OUTSIDE the lock (avoid alien method call under lock)
        if (shouldShockAuto) {
            this.shockKidnapped();
        }

        if (shouldShockGPS && gpsCollar != null) {
            this.shockKidnapped(
                " Return back to your allowed area!",
                GameConstants.DEFAULT_SHOCK_DAMAGE
            );
            warnOwnersGPSViolation(gpsCollar, gpsStack);
        }
    }

    /**
     * Sends a global alert to masters when a slave violates their GPS zone.
     * Private helper method.
     */
    private void warnOwnersGPSViolation(ItemGpsCollar gps, ItemStack stack) {
        Player player = host.getPlayer();
        if (player.getServer() == null) return;

        // Format: "ALERT: <player name> is outside the safe zone!"
        String alertMessage = String.format(
            SystemMessageManager.getTemplate(MessageCategory.GPS_OWNER_ALERT),
            player.getName().getString()
        );

        for (UUID ownerId : gps.getOwners(stack)) {
            ServerPlayer owner = player
                .getServer()
                .getPlayerList()
                .getPlayer(ownerId);
            if (owner != null) {
                SystemMessageManager.sendChatToPlayer(
                    owner,
                    alertMessage,
                    ChatFormatting.RED
                );
            }
        }
    }

    /**
     * Force-stops and clears any active shock timers.
     * Threading: Synchronized block protects timer access
     */
    public void resetAutoShockTimer() {
        synchronized (lockTimerAutoShock) {
            this.timerAutoShockCollar = null;
        }
    }

    // ========== Helper Methods ==========

    /**
     * Get the current collar ItemStack.
     * Epic 5F: Migrated to V2EquipmentHelper.
     */
    private ItemStack getCurrentCollar() {
        return com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            com.tiedup.remake.v2.BodyRegionV2.NECK
        );
    }
}
