package com.livingworld.regions;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * Manages region lifecycle state transitions.
 *
 * <p>This is a plain Java utility class that provides static methods for validating
 * and executing transitions between {@link RegionLifecycleState} values. All allowed
 * transitions are hardcoded in an internal lookup table.</p>
 *
 * <p>Allowed transitions:
 * <ul>
 *   <li>UNLOADED → LOADING</li>
 *   <li>LOADING → ACTIVE</li>
 *   <li>LOADING → FAILED</li>
 *   <li>ACTIVE → DIRTY</li>
 *   <li>DIRTY → SAVING</li>
 *   <li>SAVING → ACTIVE</li>
 *   <li>SAVING → FAILED</li>
 *   <li>ACTIVE → UNLOADING</li>
 *   <li>UNLOADING → UNLOADED</li>
 *   <li>FAILED → LOADING</li>
 * </ul></p>
 */
public class RegionLifecycleController {

    private static final String SEPARATOR = "->";

    /** Unmodifiable set of allowed transition keys in the format "FROM->TO". */
    private static final Set<String> ALLOWED_TRANSITIONS;

    static {
        Set<String> table = new HashSet<>();
        table.add("UNLOADED" + SEPARATOR + "LOADING");
        table.add("LOADING" + SEPARATOR + "ACTIVE");
        table.add("LOADING" + SEPARATOR + "FAILED");
        table.add("ACTIVE" + SEPARATOR + "DIRTY");
        table.add("DIRTY" + SEPARATOR + "SAVING");
        table.add("SAVING" + SEPARATOR + "ACTIVE");
        table.add("SAVING" + SEPARATOR + "FAILED");
        table.add("ACTIVE" + SEPARATOR + "UNLOADING");
        table.add("UNLOADING" + SEPARATOR + "UNLOADED");
        table.add("FAILED" + SEPARATOR + "LOADING");
        ALLOWED_TRANSITIONS = Collections.unmodifiableSet(table);
    }

    /**
     * Private constructor to prevent instantiation.
     */
    public RegionLifecycleController() {
    }

    // ------------------------------------------------------------------
    // Transition validation
    // ------------------------------------------------------------------

    /**
     * Returns whether the specified transition from {@code from} to {@code to} is allowed.
     *
     * @param from the current lifecycle state (must not be null)
     * @param to   the target lifecycle state (must not be null)
     * @return true if this transition is permitted
     * @throws IllegalArgumentException if either parameter is null
     */
    public static boolean canTransition(RegionLifecycleState from, RegionLifecycleState to) {
        if (from == null) {
            throw new IllegalArgumentException("from state must not be null");
        }
        if (to == null) {
            throw new IllegalArgumentException("to state must not be null");
        }

        String key = from.name() + SEPARATOR + to.name();
        return ALLOWED_TRANSITIONS.contains(key);
    }

    // ------------------------------------------------------------------
    // Transition execution
    // ------------------------------------------------------------------

    /**
     * Transitions the given region to the target lifecycle state.
     *
     * <p>Validates that the transition from the region's current state to the
     * target is allowed. If valid, updates the region's lifecycle state and marks
     * it dirty.</p>
     *
     * @param region  the region to transition (must not be null)
     * @param target  the target lifecycle state (must not be null)
     * @throws IllegalArgumentException if region or target is null
     * @throws IllegalStateException    if the transition is not allowed
     */
    public static void transition(Region region, RegionLifecycleState target) {
        if (region == null) {
            throw new IllegalArgumentException("region must not be null");
        }
        if (target == null) {
            throw new IllegalArgumentException("target state must not be null");
        }

        RegionLifecycleState current = region.getLifecycleState();

        if (!canTransition(current, target)) {
            throw new IllegalStateException(
                "Invalid lifecycle transition: " + current.name() + " -> " + target.name());
        }

        region.setLifecycleState(target);
        region.markDirty();
    }
}
