package p.studio.projectsessions;

import org.junit.jupiter.api.Test;
import p.studio.execution.StudioExecutionSessionService;
import p.studio.lsp.api.LspServerLifecycle;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.messages.LspServerConfiguration;
import p.studio.projects.ProjectReference;
import p.studio.projectstate.ProjectLocalStudioSetup;
import p.studio.projectstate.ProjectLocalStudioState;
import p.studio.projectstate.ProjectLocalStudioStateService;

import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;

final class StudioProjectSessionTest {
    @Test
    void closeSavesStateOnlyOnce() {
        final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
        final StudioProjectSession session = new StudioProjectSession(
                projectReference(),
                stateService,
                ProjectLocalStudioSetup.defaults(),
                new StudioExecutionSessionService(),
                null,
                ProjectLocalStudioState.defaults());

        session.close();
        session.close();

        assertEquals(1, stateService.saveCalls);
        assertEquals(projectReference(), stateService.savedProjectReference);
    }

    @Test
    void replaceProjectLocalStudioStateUpdatesTheSavedSnapshot() {
        final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
        final StudioProjectSession session = new StudioProjectSession(
                projectReference(),
                stateService,
                ProjectLocalStudioSetup.defaults(),
                new StudioExecutionSessionService(),
                null,
                ProjectLocalStudioState.defaults());
        final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
                .withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));

        session.replaceProjectLocalStudioState(nextState);
        session.close();

        assertSame(nextState, stateService.savedState);
    }

    @Test
    void closeShutsDownLspServerOnlyOnce() {
        final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
        final RecordingLspServerLifecycle lspServerLifecycle = new RecordingLspServerLifecycle();
        final StudioProjectSession session = new StudioProjectSession(
                projectReference(),
                stateService,
                ProjectLocalStudioSetup.defaults(),
                new StudioExecutionSessionService(),
                lspServerLifecycle,
                ProjectLocalStudioState.defaults());

        session.close();
        session.close();

        assertEquals(1, lspServerLifecycle.shutdownCalls);
    }

    private ProjectReference projectReference() {
        return new ProjectReference("Example", "1.0.0", "pbs", 1, Path.of("/tmp/example"));
    }

    private static final class RecordingProjectLocalStudioStateService extends ProjectLocalStudioStateService {
        private int saveCalls;
        private ProjectReference savedProjectReference;
        private ProjectLocalStudioState savedState;

        @Override
        public void save(final ProjectReference projectReference, final ProjectLocalStudioState state) {
            saveCalls++;
            savedProjectReference = projectReference;
            savedState = state;
        }
    }

    private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
        private int shutdownCalls;

        @Override
        public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void shutdownServer(final LspProjectContext project) {
            shutdownCalls++;
        }
    }
}
