package com.tiedup.remake.prison;

import com.tiedup.remake.core.TiedUpMod;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;

/**
 * Defines valid state transitions for prisoners.
 *
 * All transitions must go through this class to ensure consistency.
 * Invalid transitions are rejected with logging.
 */
public class PrisonerTransition {

    /**
     * Valid transitions from each state.
     *
     * State diagram:
     *
     *  ┌─────────┐    capture    ┌──────────┐    imprison    ┌────────────┐
     *  │  FREE   │──────────────►│ CAPTURED │───────────────►│ IMPRISONED │
     *  └─────────┘               └──────────┘                └────────────┘
     *       ▲                                                 │    │    ▲
     *       │                                          extract│    │    │return
     *       │  expire                                         ▼    │    │
     *  ┌────────────┐◄───────────────────────────────────┌─────────┐   │
     *  │ PROTECTED  │        release                     │ WORKING │───┘
     *  └────────────┘                                    └─────────┘
     *       │                                                 │
     *       │ expire                                          │escape
     *       ▼                                                 ▼
     *  ┌─────────┐◄───────────────────────────────────────────┘
     *  │  FREE   │           escape (distance/timeout)
     *  └─────────┘
     */
    private static final Map<
        PrisonerState,
        Set<PrisonerState>
    > VALID_TRANSITIONS = Map.of(
        PrisonerState.FREE,
        EnumSet.of(
            PrisonerState.CAPTURED, // Kidnapper captures player
            PrisonerState.PROTECTED // Admin command or special case
        ),
        PrisonerState.CAPTURED,
        EnumSet.of(
            PrisonerState.IMPRISONED, // Delivered to cell
            PrisonerState.FREE // Escape during transport
        ),
        PrisonerState.IMPRISONED,
        EnumSet.of(
            PrisonerState.WORKING, // Extracted for labor
            PrisonerState.PROTECTED, // Released with grace period
            PrisonerState.FREE // Escape (distance, collar removed)
        ),
        PrisonerState.WORKING,
        EnumSet.of(
            PrisonerState.IMPRISONED, // Returned to cell after task
            PrisonerState.PROTECTED, // Ransom paid during work
            PrisonerState.FREE // Escape (distance, timeout)
        ),
        PrisonerState.PROTECTED,
        EnumSet.of(
            PrisonerState.FREE // Protection expired
        )
    );

    /**
     * Attempt a state transition.
     *
     * @param record The prisoner record to modify
     * @param newState The target state
     * @param currentTime Current game time
     * @param playerId Player UUID (for logging)
     * @return true if transition was valid and applied
     */
    public static boolean transition(
        PrisonerRecord record,
        PrisonerState newState,
        long currentTime,
        java.util.UUID playerId
    ) {
        PrisonerState oldState = record.getState();

        // Same state is a no-op
        if (oldState == newState) {
            return true;
        }

        // Check if transition is valid
        Set<PrisonerState> validTargets = VALID_TRANSITIONS.get(oldState);
        if (validTargets == null || !validTargets.contains(newState)) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Invalid transition {} -> {} for player {}",
                oldState,
                newState,
                playerId != null ? playerId.toString().substring(0, 8) : "null"
            );
            return false;
        }

        // Apply transition
        record.setState(newState, currentTime);

        // Clean up state-specific data on certain transitions
        switch (newState) {
            case FREE:
                // Clear all ownership data
                record.setCaptorId(null);
                record.setCampId(null);
                record.setCellId(null);
                record.setProtectionExpiry(0);
                break;
            case PROTECTED:
                // Keep camp/cell data in case protection expires and they're recaptured
                // Protection expiry should be set by caller
                break;
            default:
                // No automatic cleanup for other states
                break;
        }

        TiedUpMod.LOGGER.debug(
            "[PrisonerTransition] {} -> {} for player {}",
            oldState,
            newState,
            playerId != null ? playerId.toString().substring(0, 8) : "null"
        );

        return true;
    }

    /**
     * Force a state transition without validation.
     * Use sparingly - only for admin commands or initialization.
     *
     * @param record The prisoner record to modify
     * @param newState The target state
     * @param currentTime Current game time
     */
    public static void forceTransition(
        PrisonerRecord record,
        PrisonerState newState,
        long currentTime
    ) {
        PrisonerState oldState = record.getState();
        record.setState(newState, currentTime);

        // Still clean up on FREE
        if (newState == PrisonerState.FREE) {
            record.setCaptorId(null);
            record.setCampId(null);
            record.setCellId(null);
            record.setProtectionExpiry(0);
        }

        TiedUpMod.LOGGER.info(
            "[PrisonerTransition] FORCED {} -> {}",
            oldState,
            newState
        );
    }

    /**
     * Check if a transition is valid without applying it.
     *
     * @param fromState Current state
     * @param toState Target state
     * @return true if transition would be valid
     */
    public static boolean isValidTransition(
        PrisonerState fromState,
        PrisonerState toState
    ) {
        if (fromState == toState) {
            return true;
        }
        Set<PrisonerState> validTargets = VALID_TRANSITIONS.get(fromState);
        return validTargets != null && validTargets.contains(toState);
    }

    /**
     * Get all valid target states from a given state.
     *
     * @param fromState Current state
     * @return Set of valid target states
     */
    public static Set<PrisonerState> getValidTargets(PrisonerState fromState) {
        Set<PrisonerState> targets = VALID_TRANSITIONS.get(fromState);
        return targets != null
            ? EnumSet.copyOf(targets)
            : EnumSet.noneOf(PrisonerState.class);
    }

    // ==================== CONVENIENCE METHODS ====================

    /**
     * Capture a free player.
     * Sets captor ID and transitions to CAPTURED.
     */
    public static boolean capture(
        PrisonerRecord record,
        java.util.UUID captorId,
        long currentTime,
        java.util.UUID playerId
    ) {
        if (record.getState() != PrisonerState.FREE) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Cannot capture - not FREE"
            );
            return false;
        }

        record.setCaptorId(captorId);
        return transition(
            record,
            PrisonerState.CAPTURED,
            currentTime,
            playerId
        );
    }

    /**
     * Imprison a captured player in a cell.
     * Sets camp and cell IDs and transitions to IMPRISONED.
     */
    public static boolean imprison(
        PrisonerRecord record,
        java.util.UUID campId,
        java.util.UUID cellId,
        long currentTime,
        java.util.UUID playerId
    ) {
        if (record.getState() != PrisonerState.CAPTURED) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Cannot imprison - not CAPTURED"
            );
            return false;
        }

        record.setCampId(campId);
        record.setCellId(cellId);
        return transition(
            record,
            PrisonerState.IMPRISONED,
            currentTime,
            playerId
        );
    }

    /**
     * Extract prisoner from cell for labor.
     * Transitions to WORKING.
     */
    public static boolean extract(
        PrisonerRecord record,
        long currentTime,
        java.util.UUID playerId
    ) {
        if (record.getState() != PrisonerState.IMPRISONED) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Cannot extract - not IMPRISONED"
            );
            return false;
        }

        return transition(record, PrisonerState.WORKING, currentTime, playerId);
    }

    /**
     * Return prisoner to cell after labor.
     * Transitions to IMPRISONED.
     */
    public static boolean returnToCell(
        PrisonerRecord record,
        long currentTime,
        java.util.UUID playerId
    ) {
        if (record.getState() != PrisonerState.WORKING) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Cannot return - not WORKING"
            );
            return false;
        }

        return transition(
            record,
            PrisonerState.IMPRISONED,
            currentTime,
            playerId
        );
    }

    /**
     * Release prisoner with grace period.
     * Sets protection expiry and transitions to PROTECTED.
     *
     * @param gracePeriodTicks Duration of protection in ticks (default: 6000 = 5 min)
     */
    public static boolean release(
        PrisonerRecord record,
        long currentTime,
        long gracePeriodTicks,
        java.util.UUID playerId
    ) {
        PrisonerState current = record.getState();
        if (
            current != PrisonerState.IMPRISONED &&
            current != PrisonerState.WORKING
        ) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Cannot release - not IMPRISONED or WORKING"
            );
            return false;
        }

        if (gracePeriodTicks <= 0) {
            // No grace period — go straight to FREE
            record.setProtectionExpiry(0);
            return transition(
                record,
                PrisonerState.FREE,
                currentTime,
                playerId
            );
        }

        record.setProtectionExpiry(currentTime + gracePeriodTicks);
        return transition(
            record,
            PrisonerState.PROTECTED,
            currentTime,
            playerId
        );
    }

    /**
     * Escape from captivity.
     * Transitions to FREE, clearing all ownership data.
     */
    public static boolean escape(
        PrisonerRecord record,
        long currentTime,
        java.util.UUID playerId,
        String reason
    ) {
        PrisonerState current = record.getState();
        if (!current.isCaptive() && current != PrisonerState.PROTECTED) {
            TiedUpMod.LOGGER.warn(
                "[PrisonerTransition] Cannot escape - not captive"
            );
            return false;
        }

        TiedUpMod.LOGGER.info(
            "[PrisonerTransition] Player {} escaped: {}",
            playerId != null ? playerId.toString().substring(0, 8) : "null",
            reason
        );

        return transition(record, PrisonerState.FREE, currentTime, playerId);
    }
}
