package com.livingworld.core.simulation;

import java.util.Comparator;
import java.util.Set;

import com.livingworld.regions.RegionCoordinate;

/**
 * Immutable job representing a pending region update request.
 * 
 * <p>A RegionUpdateJob captures why an update is needed and when it was queued,
 * along with which modules should be re-evaluated for that region.</p>
 */
public record RegionUpdateJob(
        RegionCoordinate coordinate,
        int priority,
        long queuedAtSimulationTick,
        Set<String> requestedModules,
        UpdateReason reason
) {

    /**
     * Creates a new region update job.
     * 
     * @param coordinate the region coordinate (must not be null)
     * @param priority    priority for ordering jobs (higher = more urgent; must be non-negative)
     * @param queuedAtSimulationTick timestamp of when this job was queued (must be non-negative)
     * @param requestedModules set of module names to update (null becomes empty set)
     * @param reason      reason for the update request (must not be null)
     */
    public RegionUpdateJob {
        if (coordinate == null) {
            throw new IllegalArgumentException("coordinate must not be null");
        }
        if (priority < 0) {
            throw new IllegalArgumentException("priority must be non-negative");
        }
        if (queuedAtSimulationTick < 0L) {
            throw new IllegalArgumentException("queuedAtSimulationTick must be non-negative");
        }
        if (reason == null) {
            throw new IllegalArgumentException("reason must not be null");
        }
        requestedModules = requestedModules == null ? Set.of() : Set.copyOf(requestedModules);
    }

    /**
     * Comparator for sorting jobs by priority in descending order.
     * Higher priority values are processed first.
     */
    public static final Comparator<RegionUpdateJob> BY_PRIORITY_DESC =
            Comparator.comparingInt(RegionUpdateJob::priority)
                    .reversed()
                    .thenComparingLong(RegionUpdateJob::queuedAtSimulationTick)
                    .thenComparing(job -> job.coordinate().stableId())
                    .thenComparing(job -> job.reason().name());
}
