package p.studio.projectsessions;

import p.studio.execution.StudioExecutionSessionService;
import p.studio.lsp.api.LspServerLifecycle;
import p.studio.projects.ProjectReference;
import p.studio.projectstate.ProjectLocalStudioSetup;
import p.studio.projectstate.ProjectLocalStudioState;
import p.studio.projectstate.ProjectLocalStudioStateService;

import java.util.Objects;

public final class StudioProjectSession implements AutoCloseable {
    private final ProjectReference projectReference;
    private final ProjectLocalStudioStateService projectLocalStudioStateService;
    private final ProjectLocalStudioSetup projectLocalStudioSetup;
    private final StudioExecutionSessionService executionSessionService;
    private final LspServerLifecycle lspServerLifecycle;
    private ProjectLocalStudioState projectLocalStudioState;
    private boolean closed;

    public StudioProjectSession(
            final ProjectReference projectReference) {
        this(
                projectReference,
                new ProjectLocalStudioStateService(),
                ProjectLocalStudioSetup.defaults(),
                new StudioExecutionSessionService(),
                null,
                ProjectLocalStudioState.defaults());
    }

    StudioProjectSession(
            final ProjectReference projectReference,
            final ProjectLocalStudioStateService projectLocalStudioStateService,
            final ProjectLocalStudioSetup projectLocalStudioSetup,
            final StudioExecutionSessionService executionSessionService,
            final LspServerLifecycle lspServerLifecycle,
            final ProjectLocalStudioState projectLocalStudioState) {
        this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
        this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
        this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
        this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
        this.lspServerLifecycle = lspServerLifecycle;
        this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
    }

    public ProjectReference projectReference() {
        return projectReference;
    }

    public ProjectLocalStudioState projectLocalStudioState() {
        return projectLocalStudioState;
    }

    public ProjectLocalStudioSetup projectLocalStudioSetup() {
        return projectLocalStudioSetup;
    }

    public StudioExecutionSessionService executionSession() {
        return executionSessionService;
    }

    public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
        this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
    }

    public void saveProjectLocalStudioState() {
        projectLocalStudioStateService.save(projectReference, projectLocalStudioState);
    }

    @Override
    public void close() {
        if (closed) {
            return;
        }
        closed = true;
        RuntimeException failure = null;
        try {
            saveProjectLocalStudioState();
        } catch (RuntimeException runtimeException) {
            failure = runtimeException;
        }
        try {
            shutdownLspServer();
        } catch (RuntimeException runtimeException) {
            if (failure == null) {
                failure = runtimeException;
            } else {
                failure.addSuppressed(runtimeException);
            }
        } finally {
            if (failure != null) {
                throw failure;
            }
        }
    }

    private void shutdownLspServer() {
        if (lspServerLifecycle == null) {
            return;
        }
        lspServerLifecycle.shutdownServer(projectReference.toLspProjectContext());
    }
}
