package com.tiedup.remake.minigame;

import com.tiedup.remake.core.TiedUpMod;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import org.jetbrains.annotations.Nullable;

/**
 * Manages lockpick mini-game sessions.
 *
 * <p>Extracted from {@link MiniGameSessionManager} (M15 split) to give lockpick
 * sessions their own focused manager. Singleton, thread-safe via ConcurrentHashMap.
 */
public class LockpickSessionManager {

    private static final LockpickSessionManager INSTANCE =
        new LockpickSessionManager();

    /**
     * Active lockpick mini-game sessions by player UUID
     */
    private final Map<UUID, LockpickMiniGameState> lockpickSessions =
        new ConcurrentHashMap<>();

    private LockpickSessionManager() {}

    public static LockpickSessionManager getInstance() {
        return INSTANCE;
    }

    /**
     * Start a new lockpick session for a player. Replaces any existing
     * session (ESC-cancel case).
     *
     * @param player          The server player
     * @param targetKind      Whether the target is a body region or a seat
     * @param targetData      BodyRegionV2 ordinal or entity id, depending on kind
     * @param sweetSpotWidth  Width of the sweet spot (based on tool)
     * @return The new session
     */
    public LockpickMiniGameState startLockpickSession(
        ServerPlayer player,
        LockpickTargetKind targetKind,
        int targetData,
        float sweetSpotWidth
    ) {
        UUID playerId = player.getUUID();

        // Check for existing session - remove it (handles ESC cancel case)
        LockpickMiniGameState existing = lockpickSessions.get(playerId);
        if (existing != null) {
            TiedUpMod.LOGGER.debug(
                "[LockpickSessionManager] Replacing existing lockpick session for {}",
                player.getName().getString()
            );
            lockpickSessions.remove(playerId);
        }

        // Create new session
        LockpickMiniGameState session = new LockpickMiniGameState(
            playerId,
            targetKind,
            targetData,
            sweetSpotWidth
        );
        lockpickSessions.put(playerId, session);

        TiedUpMod.LOGGER.info(
            "[LockpickSessionManager] Started lockpick session {} for {} (kind: {}, data: {}, width: {}%)",
            session.getSessionId().toString().substring(0, 8),
            player.getName().getString(),
            targetKind,
            targetData,
            (int) (sweetSpotWidth * 100)
        );

        // Notify nearby guards about lockpicking attempt
        GuardNotificationHelper.notifyNearbyKidnappersOfStruggle(player);

        return session;
    }

    /**
     * Play lockpick attempt sound and notify guards.
     * Called when player attempts to pick (tests position).
     *
     * @param player The player attempting to pick
     */
    public void onLockpickAttempt(ServerPlayer player) {
        // Play metallic clicking sound
        player
            .serverLevel()
            .playSound(
                null,
                player.getX(),
                player.getY(),
                player.getZ(),
                SoundEvents.CHAIN_HIT,
                SoundSource.PLAYERS,
                0.6f,
                1.2f + player.getRandom().nextFloat() * 0.3f
            );

        // Notify guards
        GuardNotificationHelper.notifyNearbyKidnappersOfStruggle(player);
    }

    /**
     * Get active lockpick session for a player.
     *
     * @param playerId The player UUID
     * @return The session, or null if none active
     */
    @Nullable
    public LockpickMiniGameState getLockpickSession(UUID playerId) {
        LockpickMiniGameState session = lockpickSessions.get(playerId);
        if (session != null && session.isExpired()) {
            lockpickSessions.remove(playerId);
            return null;
        }
        return session;
    }

    /**
     * Validate a lockpick session.
     *
     * @param playerId The player UUID
     * @param sessionId The session UUID to validate
     * @return true if session is valid and active
     */
    public boolean validateLockpickSession(UUID playerId, UUID sessionId) {
        LockpickMiniGameState session = getLockpickSession(playerId);
        if (session == null) {
            return false;
        }
        return session.getSessionId().equals(sessionId);
    }

    /**
     * End a lockpick session.
     *
     * @param playerId The player UUID
     * @param success Whether the session ended in success
     */
    public void endLockpickSession(UUID playerId, boolean success) {
        LockpickMiniGameState session = lockpickSessions.remove(playerId);
        if (session != null) {
            TiedUpMod.LOGGER.info(
                "[LockpickSessionManager] Ended lockpick session for player {} (success: {})",
                playerId.toString().substring(0, 8),
                success
            );
        }
    }

    /**
     * Clean up all lockpick sessions for a player (called on disconnect).
     *
     * @param playerId The player UUID
     */
    public void cleanupPlayer(UUID playerId) {
        lockpickSessions.remove(playerId);
    }

    /**
     * Periodic cleanup of expired lockpick sessions.
     * Should be called from server tick handler.
     */
    public void tickCleanup(long currentTick) {
        // Only run every 100 ticks (5 seconds)
        if (currentTick % 100 != 0) {
            return;
        }

        lockpickSessions
            .entrySet()
            .removeIf(entry -> entry.getValue().isExpired());
    }

    /**
     * Get count of active lockpick sessions (for debugging).
     */
    public int getActiveSessionCount() {
        return lockpickSessions.size();
    }
}
