package com.livingworld.config;

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

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

/**
 * Unit tests for {@link SimulationConfig}.
 */
class SimulationConfigTest {

    // ------------------------------------------------------------------
    // Default values
    // ------------------------------------------------------------------

    @Nested
    @DisplayName("Default values")
    class DefaultValues {

        @Test
        void defaultRegionSizeChunks() {
            final SimulationConfig config = new SimulationConfig();
            assertEquals(8, config.getRegionSizeChunks());
        }

        @Test
        void defaultSimulationIntervalTicks() {
            final SimulationConfig config = new SimulationConfig();
            assertEquals(100, config.getSimulationIntervalTicks());
        }

        @Test
        void defaultMaxRegionsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            assertEquals(50, config.getMaxRegionsPerCycle());
        }

        @Test
        void defaultMaxMillisecondsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            assertEquals(25, config.getMaxMillisecondsPerCycle());
        }

        @Test
        void defaultEmergencyStopMilliseconds() {
            final SimulationConfig config = new SimulationConfig();
            assertEquals(40, config.getEmergencyStopMilliseconds());
        }

        @Test
        void defaultEnableDebugCommands() {
            final SimulationConfig config = new SimulationConfig();
            assertTrue(config.isEnableDebugCommands());
        }

        @Test
        void defaultEnableProfiler() {
            final SimulationConfig config = new SimulationConfig();
            assertTrue(config.isEnableProfiler());
        }
    }

    // ------------------------------------------------------------------
    // Validation – valid values should pass
    // ------------------------------------------------------------------

    @Nested
    @DisplayName("Validation with valid values")
    class ValidValues {

        @Test
        void defaultsAreValid() {
            final SimulationConfig config = new SimulationConfig();
            config.validate();  // Should not throw
        }

        @Test
        void minimumRegionSizeChunks() {
            final SimulationConfig config = new SimulationConfig();
            config.setRegionSizeChunks(1);
            config.validate();  // Should not throw
        }

        @Test
        void minimumSimulationIntervalTicks() {
            final SimulationConfig config = new SimulationConfig();
            config.setSimulationIntervalTicks(1);
            config.validate();  // Should not throw
        }

        @Test
        void minimumMaxRegionsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxRegionsPerCycle(1);
            config.validate();  // Should not throw
        }

        @Test
        void minimumMaxMillisecondsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxMillisecondsPerCycle(1);
            config.validate();  // Should not throw
        }

        @Test
        void emergencyStopEqualsMaxMilliseconds() {
            final SimulationConfig config = new SimulationConfig();
            config.setEmergencyStopMilliseconds(25);
            config.setMaxMillisecondsPerCycle(25);
            config.validate();  // Should not throw (equal is allowed)
        }

        @Test
        void emergencyStopGreaterThanMaxMilliseconds() {
            final SimulationConfig config = new SimulationConfig();
            config.setEmergencyStopMilliseconds(50);
            config.setMaxMillisecondsPerCycle(25);
            config.validate();  // Should not throw
        }
    }

    // ------------------------------------------------------------------
    // Validation – invalid values should fail
    // ------------------------------------------------------------------

    @Nested
    @DisplayName("Validation with invalid values")
    class InvalidValues {

        @Test
        void zeroRegionSizeChunks() {
            final SimulationConfig config = new SimulationConfig();
            config.setRegionSizeChunks(0);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void negativeRegionSizeChunks() {
            final SimulationConfig config = new SimulationConfig();
            config.setRegionSizeChunks(-5);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void zeroSimulationIntervalTicks() {
            final SimulationConfig config = new SimulationConfig();
            config.setSimulationIntervalTicks(0);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void negativeSimulationIntervalTicks() {
            final SimulationConfig config = new SimulationConfig();
            config.setSimulationIntervalTicks(-10);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void zeroMaxRegionsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxRegionsPerCycle(0);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void negativeMaxRegionsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxRegionsPerCycle(-1);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void zeroMaxMillisecondsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxMillisecondsPerCycle(0);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void negativeMaxMillisecondsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxMillisecondsPerCycle(-10);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void emergencyStopLessThanMaxMilliseconds() {
            final SimulationConfig config = new SimulationConfig();
            config.setEmergencyStopMilliseconds(20);
            config.setMaxMillisecondsPerCycle(25);
            assertThrows(IllegalArgumentException.class, () -> config.validate());
        }

        @Test
        void emergencyStopMessageContainsValues() {
            final SimulationConfig config = new SimulationConfig();
            config.setEmergencyStopMilliseconds(10);
            config.setMaxMillisecondsPerCycle(30);
            final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> config.validate());
            assertTrue(exception.getMessage().contains("emergencyStopMilliseconds"));
            assertTrue(exception.getMessage().contains("maxMillisecondsPerCycle"));
        }
    }

    // ------------------------------------------------------------------
    // Setters
    // ------------------------------------------------------------------

    @Nested
    @DisplayName("Setters")
    class Setters {

        @Test
        void setRegionSizeChunks() {
            final SimulationConfig config = new SimulationConfig();
            config.setRegionSizeChunks(16);
            assertEquals(16, config.getRegionSizeChunks());
        }

        @Test
        void setSimulationIntervalTicks() {
            final SimulationConfig config = new SimulationConfig();
            config.setSimulationIntervalTicks(50);
            assertEquals(50, config.getSimulationIntervalTicks());
        }

        @Test
        void setMaxRegionsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxRegionsPerCycle(100);
            assertEquals(100, config.getMaxRegionsPerCycle());
        }

        @Test
        void setMaxMillisecondsPerCycle() {
            final SimulationConfig config = new SimulationConfig();
            config.setMaxMillisecondsPerCycle(50);
            assertEquals(50, config.getMaxMillisecondsPerCycle());
        }

        @Test
        void setEmergencyStopMilliseconds() {
            final SimulationConfig config = new SimulationConfig();
            config.setEmergencyStopMilliseconds(60);
            assertEquals(60, config.getEmergencyStopMilliseconds());
        }

        @Test
        void setEnableDebugCommandsFalse() {
            final SimulationConfig config = new SimulationConfig();
            config.setEnableDebugCommands(false);
            assertFalse(config.isEnableDebugCommands());
        }

        @Test
        void setEnableProfilerFalse() {
            final SimulationConfig config = new SimulationConfig();
            config.setEnableProfiler(false);
            assertFalse(config.isEnableProfiler());
        }
    }

    // ------------------------------------------------------------------
    // toString
    // ------------------------------------------------------------------

    @Test
    @DisplayName("toString should not throw")
    void toStringShouldNotThrow() {
        final SimulationConfig config = new SimulationConfig();
        final String result = config.toString();
        assertTrue(result.contains("SimulationConfig"));
        assertTrue(result.contains("regionSizeChunks=8"));
        assertTrue(result.contains("simulationIntervalTicks=100"));
    }
}