package com.livingworld.core.services;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.livingworld.regions.Region;
import com.livingworld.regions.RegionCoordinate;
import com.livingworld.regions.RegionFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class FileRegionPersistenceServiceTest {

    private static final Clock CLOCK =
            Clock.fixed(Instant.ofEpochMilli(1_000L), ZoneOffset.UTC);

    @TempDir
    Path temporaryDirectory;

    @Test
    void roundTripsCoreRegionState() {
        FileRegionPersistenceService service = service();
        Region original = new RegionFactory().createNewRegion(
                new RegionCoordinate("minecraft:overworld", -2, 3),
                12);
        original.updateLastSimulatedTick(42);
        original.getFlags().setHasHighPollution(true);
        original.getFlags().setForceLoadedBySimulation(true);
        original.getMetrics().setEcosystemHealth(37.5);
        original.getMetrics().setWaterQuality(22.25);

        service.saveRegion(original);
        Optional<Region> loaded = service.loadRegion(original.getCoordinate());

        assertTrue(loaded.isPresent());
        Region restored = loaded.orElseThrow();
        assertEquals(original.getId(), restored.getId());
        assertEquals(original.getCoordinate(), restored.getCoordinate());
        assertEquals(original.getLifecycleState(), restored.getLifecycleState());
        assertEquals(12, restored.getCreatedAtSimulationTick());
        assertEquals(42, restored.getLastUpdatedSimulationTick());
        assertTrue(restored.getFlags().isHasHighPollution());
        assertTrue(restored.getFlags().isForceLoadedBySimulation());
        assertEquals(37.5, restored.getMetrics().getEcosystemHealth());
        assertEquals(22.25, restored.getMetrics().getWaterQuality());
        assertFalse(restored.isDirty());
    }

    @Test
    void dirtyQueueIsPersistedAndCleared() {
        FileRegionPersistenceService service = service();
        Region region = new RegionFactory().createNewRegion(
                new RegionCoordinate("minecraft:overworld", 0, 0),
                0);

        service.markRegionDirty(region);
        assertEquals(1, service.getDirtyRegionCount());

        service.saveDirtyRegions();

        assertEquals(0, service.getDirtyRegionCount());
        assertFalse(region.isDirty());
        assertTrue(service.loadRegion(region.getCoordinate()).isPresent());
    }

    @Test
    void corruptRegionIsQuarantinedInsteadOfCrashingLoad() throws Exception {
        FileRegionPersistenceService service = service();
        Region region = new RegionFactory().createNewRegion(
                new RegionCoordinate("minecraft:overworld", 1, 0),
                0);
        service.saveRegion(region);
        Path regionFile;
        try (var files = Files.list(temporaryDirectory.resolve("regions"))) {
            regionFile = files.findFirst().orElseThrow();
        }
        Files.writeString(regionFile, "not=a-valid-region");

        assertTrue(service.loadRegion(region.getCoordinate()).isEmpty());
        try (var files = Files.list(temporaryDirectory.resolve("regions"))) {
            assertTrue(files.anyMatch(path -> path.getFileName().toString().contains(".corrupt-")));
        }
    }

    @Test
    void metadataSurvivesServiceRestart() {
        service().flushAll();

        FileRegionPersistenceService restarted = service();

        assertTrue(Files.exists(temporaryDirectory.resolve("metadata.properties")));
        assertEquals(0, restarted.getDirtyRegionCount());
    }

    private FileRegionPersistenceService service() {
        return new FileRegionPersistenceService(
                temporaryDirectory,
                "0.1.0",
                CLOCK);
    }
}
