package com.livingworld.core.simulation;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.Test;

import com.livingworld.config.SimulationConfig;
import com.livingworld.regions.RegionCoordinate;

class SimulationSchedulerTest {

    @Test
    void pollsHighestPriorityFirstAndRespectsRegionBudget() {
        SimulationConfig config = new SimulationConfig();
        config.setMaxRegionsPerCycle(3);
        SimulationScheduler scheduler = new SimulationScheduler(config);

        scheduler.queueRegion(job(2, 0));
        scheduler.queueRegion(job(10, 1));
        scheduler.queueRegion(job(5, 2));
        scheduler.queueRegion(job(8, 3));

        List<RegionUpdateJob> jobs = scheduler.pollJobsForCycle();

        assertEquals(List.of(10, 8, 5), jobs.stream().map(RegionUpdateJob::priority).toList());
        assertEquals(1, scheduler.getQueuedJobCount());
    }

    @Test
    void equalPriorityJobsUseQueueTickAsDeterministicTieBreaker() {
        SimulationScheduler scheduler = new SimulationScheduler(new SimulationConfig());
        scheduler.queueRegion(job(5, 20));
        scheduler.queueRegion(job(5, 10));

        assertEquals(
                List.of(10L, 20L),
                scheduler.pollJobsForCycle().stream()
                        .map(RegionUpdateJob::queuedAtSimulationTick)
                        .toList());
    }

    @Test
    void regionUpdateJobNormalizesAndCopiesRequestedModules() {
        List<String> mutableModules = new ArrayList<>(List.of("soil"));
        RegionUpdateJob job = new RegionUpdateJob(
                new RegionCoordinate("minecraft:overworld", 0, 0),
                1,
                0,
                Set.copyOf(mutableModules),
                UpdateReason.NORMAL_ROLLING_UPDATE);

        mutableModules.add("pollution");

        assertEquals(Set.of("soil"), job.requestedModules());
        assertThrows(UnsupportedOperationException.class,
                () -> job.requestedModules().add("water"));

        RegionUpdateJob emptyModules = new RegionUpdateJob(
                new RegionCoordinate("minecraft:overworld", 1, 0),
                1,
                0,
                null,
                UpdateReason.NORMAL_ROLLING_UPDATE);
        assertEquals(Set.of(), emptyModules.requestedModules());
    }

    @Test
    void simulationCounterAdvancesOnlyWhenCycleCompletes() {
        SimulationConfig config = new SimulationConfig();
        config.setSimulationIntervalTicks(2);
        SimulationScheduler scheduler = new SimulationScheduler(config);

        scheduler.onMinecraftTick();
        scheduler.onMinecraftTick();

        assertEquals(0, scheduler.getSimulationTickCounter());
        scheduler.recordCompletedSimulationCycle();
        assertEquals(1, scheduler.getSimulationTickCounter());
    }

    private static RegionUpdateJob job(int priority, long queuedAtTick) {
        return new RegionUpdateJob(
                new RegionCoordinate("minecraft:overworld", priority, 0),
                priority,
                queuedAtTick,
                Set.of(),
                UpdateReason.NORMAL_ROLLING_UPDATE);
    }
}
