package com.livingworld.bootstrap;

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 com.livingworld.core.services.CoreServices;
import com.livingworld.core.services.TimeService;
import com.livingworld.platform.PlatformAdapter;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class LivingWorldBootstrapTest {

    @TempDir
    Path temporaryDirectory;

    @Test
    void createsAndLocksCompleteServerServiceGraph() {
        LivingWorldBootstrap bootstrap = new LivingWorldBootstrap();
        TestPlatformAdapter platform = new TestPlatformAdapter(temporaryDirectory);

        bootstrap.initialize(platform);
        bootstrap.onCommonSetup();
        bootstrap.onServerStarting(temporaryDirectory);
        bootstrap.onServerStarted();

        assertTrue(bootstrap.isServerReady());
        assertEquals(1, platform.commandRegistrations);
        assertEquals(1, platform.tickRegistrations);
        assertTrue(bootstrap.getServices().isLocked());
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.CONFIG));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.REGIONS));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.SIMULATION));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.PERSISTENCE));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.EVENTS));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.MODULES));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.TIME));
        assertTrue(bootstrap.getServices().isRegistered(CoreServices.DEBUG));

        for (int tick = 0; tick < 50; tick++) {
            bootstrap.onServerTick();
        }
        TimeService timeService = bootstrap.getServices().get(CoreServices.TIME);
        assertEquals(1, timeService.getSimulationTick());

        bootstrap.onServerStopping();

        assertFalse(bootstrap.isServerReady());
        assertTrue(Files.exists(temporaryDirectory.resolve("living_world/metadata.properties")));
    }

    @Test
    void ignoresTicksUntilServerServicesAreReady() {
        LivingWorldBootstrap bootstrap = new LivingWorldBootstrap();
        bootstrap.initialize(new TestPlatformAdapter(temporaryDirectory));

        bootstrap.onServerTick();

        assertFalse(bootstrap.isServerReady());
    }

    @Test
    void rejectsServerLifecycleBeforeInitialization() {
        LivingWorldBootstrap bootstrap = new LivingWorldBootstrap();

        assertThrows(
                IllegalStateException.class,
                () -> bootstrap.onServerStarting(temporaryDirectory));
    }

    private static final class TestPlatformAdapter implements PlatformAdapter {
        private final Path saveDirectory;
        private int commandRegistrations;
        private int tickRegistrations;

        private TestPlatformAdapter(Path saveDirectory) {
            this.saveDirectory = saveDirectory;
        }

        @Override public String getPlatformName() { return "test"; }
        @Override public String getMinecraftVersion() { return "test"; }
        @Override public String getLoaderVersion() { return "test"; }
        @Override public boolean isDedicatedServer() { return true; }
        @Override public Path getWorldSaveDirectory() { return saveDirectory; }
        @Override public void registerCommands() { commandRegistrations++; }
        @Override public void registerServerTickHook() { tickRegistrations++; }
        @Override public void registerPlayerEventHooks() {}
    }
}
