package p.studio.lsp.services.protocol;

import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
import p.studio.lsp.messages.LspProjectContext;

import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

final class ProjectConnectionValidator {
    private ProjectConnectionValidator() {
    }

    static void validateProjectBinding(
            final LspProjectContext project,
            final InitializeParams params) {
        final LspProjectContext safeProject = Objects.requireNonNull(project, "project");
        final InitializeParams safeParams = Objects.requireNonNull(params, "params");

        final Path expectedRoot = safeProject.projectRoot().toAbsolutePath().normalize();
        final List<Path> clientRoots = extractClientRoots(safeParams);
        if (clientRoots.isEmpty()) {
            throw mismatch(safeProject, "client did not declare rootUri or workspaceFolders");
        }

        for (final Path clientRoot : clientRoots) {
            if (expectedRoot.equals(clientRoot)) {
                return;
            }
        }

        throw mismatch(safeProject, "client roots " + clientRoots + " do not match " + expectedRoot);
    }

    private static List<Path> extractClientRoots(final InitializeParams params) {
        final List<Path> roots = new ArrayList<>();
        if (params.getRootUri() != null && !params.getRootUri().isBlank()) {
            roots.add(parsePath(params.getRootUri()));
        }
        if (params.getWorkspaceFolders() != null) {
            for (final WorkspaceFolder workspaceFolder : params.getWorkspaceFolders()) {
                if (workspaceFolder == null || workspaceFolder.getUri() == null || workspaceFolder.getUri().isBlank()) {
                    continue;
                }
                roots.add(parsePath(workspaceFolder.getUri()));
            }
        }
        return List.copyOf(roots);
    }

    private static Path parsePath(final String rawUri) {
        try {
            return Path.of(URI.create(rawUri)).toAbsolutePath().normalize();
        } catch (RuntimeException exception) {
            throw new ResponseErrorException(new ResponseError(
                    ResponseErrorCode.InvalidParams,
                    "invalid project root uri from client: " + rawUri,
                    null));
        }
    }

    private static ResponseErrorException mismatch(
            final LspProjectContext project,
            final String detail) {
        return new ResponseErrorException(new ResponseError(
                ResponseErrorCode.InvalidParams,
                "Prometeu Studio LSP is bound to project root "
                        + project.projectRoot()
                        + " and rejected this client: "
                        + detail,
                null));
    }
}
