package thb.jeanluc.adventure.save;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import thb.jeanluc.adventure.game.GameSession;
import thb.jeanluc.adventure.loader.WorldLoader;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

class SaveServiceTest {

    private GameSession freshSession(String slot) {
        WorldLoader.LoadResult r = new WorldLoader().load();
        return new GameSession(r.world(), r.player(), slot);
    }

    @Test
    void saveThenLoadReproducesState(@TempDir Path dir) {
        SaveService svc = new SaveService(dir);
        GameSession s = freshSession("my-game");
        s.getState().set("power_on");
        s.getPlayer().setGold(4);
        s.setTurn(9);
        svc.save(s);

        GameSession loaded = svc.load("my-game");
        assertThat(loaded.getState().isSet("power_on")).isTrue();
        assertThat(loaded.getPlayer().getGold()).isEqualTo(4);
        assertThat(loaded.getTurn()).isEqualTo(9);
        assertThat(loaded.getSlotName()).isEqualTo("my-game");
    }

    @Test
    void listReturnsSlotMetadata(@TempDir Path dir) {
        SaveService svc = new SaveService(dir);
        GameSession s = freshSession("Alpha Run");
        s.setTurn(3);
        svc.save(s);

        List<SaveSlotInfo> slots = svc.list();
        assertThat(slots).hasSize(1);
        assertThat(slots.getFirst().slotName()).isEqualTo("Alpha Run");
        assertThat(slots.getFirst().slug()).isEqualTo("alpha_run");
        assertThat(slots.getFirst().turn()).isEqualTo(3);
    }

    @Test
    void listSkipsCorruptFiles(@TempDir Path dir) throws Exception {
        Files.writeString(dir.resolve("broken.json"), "{ not json");
        assertThat(new SaveService(dir).list()).isEmpty();
    }

    @Test
    void listExcludesSettingsFile(@TempDir Path dir) throws Exception {
        SaveService svc = new SaveService(dir);
        GameSession s = freshSession("Real Save");
        svc.save(s);
        Files.writeString(dir.resolve("settings.json"), "{\"color\":\"on\"}");

        List<SaveSlotInfo> slots = svc.list();
        assertThat(slots).hasSize(1);
        assertThat(slots.getFirst().slotName()).isEqualTo("Real Save");
    }

    @Test
    void slugForSettingsDoesNotCollideWithSettingsFile() {
        assertThat(SaveService.slug("settings")).isNotEqualTo("settings");
    }

    @Test
    void loadCorruptFileThrowsSaveException(@TempDir Path dir) throws Exception {
        Files.writeString(dir.resolve("bad.json"), "{ not json");
        SaveService svc = new SaveService(dir);
        assertThrows(SaveException.class, () -> svc.load("bad"));
    }

    @Test
    void loadRejectsIncompatibleVersion(@TempDir Path dir) throws Exception {
        // a syntactically valid SaveData JSON but with a future schemaVersion
        String json = "{\"schemaVersion\":999,\"worldTitle\":\"X\",\"slotName\":\"v\","
                + "\"savedAtEpochMillis\":0,\"turn\":0,\"currentRoomId\":\"k\","
                + "\"visitedRoomIds\":[],\"gold\":0,\"inventoryItemIds\":[],\"flags\":[],"
                + "\"questStages\":{},\"questCompleted\":[],\"roomItemIds\":{},\"switchStates\":{}}";
        Files.writeString(dir.resolve("v.json"), json);
        SaveService svc = new SaveService(dir);
        assertThrows(SaveException.class, () -> svc.load("v"));
    }
}
