package p.studio.lsp.services.protocol;

import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.InitializedParams;
import org.eclipse.lsp4j.services.*;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.services.LanguageServiceBridge;
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
import p.studio.lsp.services.protocol.mapping.Lsp4jProtocolMessageMapper;
import p.studio.lsp.services.protocol.mapping.ProtocolMessageMapper;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;

public final class PrometeuLanguageServer implements LanguageServer, LanguageClientAware {
    private final LspProjectContext project;
    private final LanguageServiceBridge languageServiceBridge;
    private final ProtocolMessageMapper protocolMessageMapper;
    private final TextDocumentService textDocumentService;
    private final WorkspaceService workspaceService;
    private volatile LanguageClient client;
    private volatile boolean shutdownRequested;

    public PrometeuLanguageServer(
            final LspProjectContext project,
            final CompilerLanguageServiceBridge compilerBridge) {
        this(project,
                compilerBridge,
                new Lsp4jProtocolMessageMapper(),
                null,
                new PrometeuWorkspaceService());
    }

    PrometeuLanguageServer(
            final LspProjectContext project,
            final LanguageServiceBridge languageServiceBridge,
            final ProtocolMessageMapper protocolMessageMapper,
            final TextDocumentService textDocumentService,
            final WorkspaceService workspaceService) {
        this.project = Objects.requireNonNull(project, "project");
        this.languageServiceBridge = Objects.requireNonNull(languageServiceBridge, "languageServiceBridge");
        this.protocolMessageMapper = Objects.requireNonNull(protocolMessageMapper, "protocolMessageMapper");
        this.textDocumentService = textDocumentService != null
                ? textDocumentService
                : new PrometeuTextDocumentService(project, languageServiceBridge, protocolMessageMapper);
        this.workspaceService = Objects.requireNonNull(workspaceService, "workspaceService");
    }

    @Override
    public void connect(final LanguageClient client) {
        this.client = client;
        if (textDocumentService instanceof PrometeuTextDocumentService service) {
            service.connect(client);
        }
    }

    @Override
    public CompletableFuture<InitializeResult> initialize(final InitializeParams params) {
        try {
            ProjectConnectionValidator.validateProjectBinding(project, params);
        } catch (RuntimeException runtimeException) {
            final LanguageClient currentClient = client;
            if (currentClient != null) {
                currentClient.logMessage(protocolMessageMapper
                        .mapErrorMessage("Failed to initialize Prometeu Studio LSP for project " + project.projectKey() + ": " + runtimeException.getMessage()));
            }
            return CompletableFuture.failedFuture(runtimeException);
        }
        return CompletableFuture.completedFuture(protocolMessageMapper
                .mapInitializeResult(languageServiceBridge.describeServer(project)));
    }

    @Override
    public void initialized(final InitializedParams params) {
        final LanguageClient currentClient = client;
        if (currentClient == null) {
            return;
        }
        currentClient.logMessage(protocolMessageMapper
                .mapInfoMessage("Prometeu Studio LSP connected for project " + project.projectKey()));
    }

    @Override
    public CompletableFuture<Object> shutdown() {
        shutdownRequested = true;
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public void exit() {
        if (!shutdownRequested) {
            final LanguageClient currentClient = client;
            if (currentClient != null) {
                currentClient.logMessage(protocolMessageMapper
                        .mapWarningMessage("Prometeu Studio LSP received exit before shutdown"));
            }
        }
    }

    @Override
    public TextDocumentService getTextDocumentService() {
        return textDocumentService;
    }

    @Override
    public WorkspaceService getWorkspaceService() {
        return workspaceService;
    }
}
