package com.livingworld.regions;

import org.junit.jupiter.api.Test;

import java.util.UUID;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Tests for {@link RegionFactory}.
 */
class RegionFactoryTest {

    private final RegionFactory factory = new RegionFactory();

    // ------------------------------------------------------------------
    // Null handling
    // ------------------------------------------------------------------

    @Test
    void createNewRegionWithNullCoordinateThrows() {
        IllegalArgumentException exception = assertThrows(
                IllegalArgumentException.class,
                () -> factory.createNewRegion(null, 0L)
        );
        assertEquals("coordinate must not be null", exception.getMessage());
    }

    // ------------------------------------------------------------------
    // Basic creation
    // ------------------------------------------------------------------

    @Test
    void createNewRegionReturnsValidRegion() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        long tick = 42L;

        Region region = factory.createNewRegion(coordinate, tick);

        assertNotNull(region);
        assertDoesNotThrow(() -> region.validate());
    }

    // ------------------------------------------------------------------
    // UUID assignment
    // ------------------------------------------------------------------

    @Test
    void createNewRegionAssignsRandomUUID() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);

        Region region1 = factory.createNewRegion(coordinate, 0L);
        Region region2 = factory.createNewRegion(coordinate, 0L);

        assertNotEquals(region1.getId(), region2.getId());
    }

    @Test
    void createNewRegionUUIDIsNotNull() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        Region region = factory.createNewRegion(coordinate, 0L);

        assertNotNull(region.getId());
        assertInstanceOf(UUID.class, region.getId());
    }

    // ------------------------------------------------------------------
    // Coordinate assignment
    // ------------------------------------------------------------------

    @Test
    void createNewRegionUsesArgumentCoordinate() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 5, -3);
        Region region = factory.createNewRegion(coordinate, 0L);

        assertEquals(coordinate, region.getCoordinate());
    }

    // ------------------------------------------------------------------
    // Lifecycle state
    // ------------------------------------------------------------------

    @Test
    void createNewRegionStartsActive() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        Region region = factory.createNewRegion(coordinate, 0L);

        assertEquals(RegionLifecycleState.ACTIVE, region.getLifecycleState());
    }

    // ------------------------------------------------------------------
    // Simulation tick values
    // ------------------------------------------------------------------

    @Test
    void createNewRegionSetsCreatedAtSimulationTick() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        long tick = 12345L;
        Region region = factory.createNewRegion(coordinate, tick);

        assertEquals(tick, region.getCreatedAtSimulationTick());
    }

    @Test
    void createNewRegionSetsLastUpdatedSimulationTick() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        long tick = 12345L;
        Region region = factory.createNewRegion(coordinate, tick);

        assertEquals(tick, region.getLastUpdatedSimulationTick());
    }

    // ------------------------------------------------------------------
    // Dirty flag
    // ------------------------------------------------------------------

    @Test
    void createNewRegionIsDirty() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        Region region = factory.createNewRegion(coordinate, 0L);

        assertTrue(region.isDirty());
    }

    // ------------------------------------------------------------------
    // Default flags
    // ------------------------------------------------------------------

    @Test
    void createNewRegionHasDefaultFlags() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        Region region = factory.createNewRegion(coordinate, 0L);

        RegionFlags flags = region.getFlags();
        assertNotNull(flags);
        assertFalse(flags.isHasPlayerActivity());
        assertFalse(flags.isHasHighPollution());
        assertFalse(flags.isHasLowSoilQuality());
        assertFalse(flags.isHasActiveEcosystemEvent());
        assertFalse(flags.isForceLoadedBySimulation());
        assertFalse(flags.isCorrupted());
    }

    // ------------------------------------------------------------------
    // Default metrics
    // ------------------------------------------------------------------

    @Test
    void createNewRegionHasDefaultMetrics() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        Region region = factory.createNewRegion(coordinate, 0L);

        RegionMetrics metrics = region.getMetrics();
        assertNotNull(metrics);

        assertEquals(60.0, metrics.getEcosystemHealth());
        assertEquals(0.0, metrics.getPollutionScore());
        assertEquals(60.0, metrics.getSoilQuality());
        assertEquals(60.0, metrics.getWaterQuality());
        assertEquals(50.0, metrics.getVegetationPressure());
        assertEquals(0.0, metrics.getResourceDepletion());
        assertEquals(50.0, metrics.getRecoveryPressure());
    }

    // ------------------------------------------------------------------
    // Empty module data
    // ------------------------------------------------------------------

    @Test
    void createNewRegionHasEmptyModuleData() {
        RegionCoordinate coordinate = new RegionCoordinate("overworld", 0, 0);
        Region region = factory.createNewRegion(coordinate, 0L);

        RegionModuleData moduleData = region.getModuleData();
        assertNotNull(moduleData);
        assertTrue(moduleData.moduleIds().isEmpty());
    }

    // ------------------------------------------------------------------
    // Different dimensions
    // ------------------------------------------------------------------

    @Test
    void createNewRegionWorksForDifferentDimensions() {
        RegionCoordinate coordOverworld = new RegionCoordinate("overworld", 0, 0);
        RegionCoordinate coordNether = new RegionCoordinate("the_nether", 1, 2);
        RegionCoordinate coordEnd = new RegionCoordinate("end", -3, -4);

        Region regionOverworld = factory.createNewRegion(coordOverworld, 0L);
        Region regionNether = factory.createNewRegion(coordNether, 0L);
        Region regionEnd = factory.createNewRegion(coordEnd, 0L);

        assertEquals(coordOverworld, regionOverworld.getCoordinate());
        assertEquals(coordNether, regionNether.getCoordinate());
        assertEquals(coordEnd, regionEnd.getCoordinate());

        assertNotEquals(regionOverworld.getId(), regionNether.getId());
        assertNotEquals(regionNether.getId(), regionEnd.getId());
    }
}