package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.execution.GrpcAsyncExecutor;
import com.atlassian.bitbucket.mesh.git.content.ContentManager;
import com.atlassian.bitbucket.mesh.git.content.GetNodeTypeParameters;
import com.atlassian.bitbucket.mesh.git.content.GetObjectInfoParameters;
import com.atlassian.bitbucket.mesh.git.content.ListDirectoryParameters;
import com.atlassian.bitbucket.mesh.grpc.BackoffStreamObserver;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.repository.RepositoryManager;
import com.atlassian.bitbucket.mesh.rpc.v1.*;
import com.atlassian.bitbucket.mesh.util.PageRange;
import com.google.protobuf.ByteString;
import io.grpc.stub.StreamObserver;

public class GrpcContentService extends ContentServiceGrpc.ContentServiceImplBase {

    private final ContentManager contentManager;
    private final GrpcAsyncExecutor executor;
    private final RepositoryManager repositoryManager;

    public GrpcContentService(ContentManager contentManager, GrpcAsyncExecutor executor,
                              RepositoryManager repositoryManager) {
        this.contentManager = contentManager;
        this.executor = executor;
        this.repositoryManager = repositoryManager;
    }

    @Override
    public void getNodeType(RpcGetNodeTypeRequest request, StreamObserver<RpcGetNodeTypeResponse> responseObserver) {
        GitNodeType nodeType = contentManager.getNodeType(buildGetNodeTypeParameters(request));

        responseObserver.onNext(RpcGetNodeTypeResponse.newBuilder()
                .setType(nodeType.toRpc())
                .build());
        responseObserver.onCompleted();
    }

    @Override
    public void getObjectInfo(RpcGetObjectInfoRequest request,
                              StreamObserver<RpcGetObjectInfoResponse> responseObserver) {
        GitObjectInfo object = contentManager.getObjectInfo(buildGetObjectInfoParameters(request));

        responseObserver.onNext(RpcGetObjectInfoResponse.newBuilder()
                .setObjectInfo(RpcGitObjectInfo.newBuilder()
                        .setObjectId(ByteString.copyFromUtf8(object.getObjectId()))
                        .setSize(object.getSize())
                        .setType(object.getType().toRpc())
                        .build())
                .build());
        responseObserver.onCompleted();
    }

    @Override
    public void listDirectory(RpcListDirectoryRequest request,
                              StreamObserver<RpcListDirectoryResponseFragment> responseObserver) {
        ObserverDirectoryCallback callback = new ObserverDirectoryCallback(
                new BackoffStreamObserver<>(responseObserver));
        ListDirectoryParameters parameters = buildListDirectoryParameters(request);
        executor.submit(responseObserver, () -> {
            contentManager.listDirectory(parameters, callback);

            callback.onEnd();
        });
    }

    private GetNodeTypeParameters buildGetNodeTypeParameters(RpcGetNodeTypeRequest request) {
        Repository repository = repositoryManager.getById(request.getRepository());

        return new GetNodeTypeParameters.Builder(repository)
                .commitId(request.getCommitish().toStringUtf8())
                .path(request.getPath().toStringUtf8())
                .timeouts(request.getTimeouts())
                .build();
    }

    private GetObjectInfoParameters buildGetObjectInfoParameters(RpcGetObjectInfoRequest request) {
        Repository repository = repositoryManager.getById(request.getRepository());

        return new GetObjectInfoParameters.Builder(repository)
                .commitId(request.getCommitish().toStringUtf8())
                .path(request.hasPath() ? request.getPath().toStringUtf8() : null)
                .timeouts(request.getTimeouts())
                .build();
    }

    private ListDirectoryParameters buildListDirectoryParameters(RpcListDirectoryRequest request) {
        Repository repository = repositoryManager.getById(request.getRepository());

        return new ListDirectoryParameters.Builder(repository)
                .commitId(request.getCommitish().toStringUtf8())
                .pageRange(new PageRange(request.getLimit(), request.getSkip()))
                .path(request.getPath().toStringUtf8())
                .recursive(request.getRecursive())
                .timeouts(request.getTimeouts())
                .withSizes(request.getSizes())
                .build();
    }
}
