package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.worktree.CreateWorkTreeParameters;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.repository.RepositoryManager;
import com.atlassian.bitbucket.mesh.repository.WorkTree;
import com.atlassian.bitbucket.mesh.repository.WorkTreeManager;
import com.atlassian.bitbucket.mesh.rpc.v1.*;
import io.grpc.stub.StreamObserver;

import java.time.Duration;
import java.util.Set;
import java.util.stream.Collectors;

public class GrpcWorkTreeService extends WorkTreeServiceGrpc.WorkTreeServiceImplBase {

    private final GitQuarantineRegistry quarantineRegistry;
    private final RepositoryManager repositoryManager;
    private final WorkTreeManager workTreeManager;

    public GrpcWorkTreeService(RepositoryManager repositoryManager, GitQuarantineRegistry quarantineRegistry,
                               WorkTreeManager workTreeManager) {
        this.repositoryManager = repositoryManager;
        this.quarantineRegistry = quarantineRegistry;
        this.workTreeManager = workTreeManager;
    }

    @Override
    public void close(RpcCloseWorkTreeRequest request, StreamObserver<RpcCloseWorkTreeResponse> responseObserver) {
        WorkTree workTree = workTreeManager.getById(request.getWorkTree());

        workTreeManager.close(workTree);

        responseObserver.onNext(RpcCloseWorkTreeResponse.getDefaultInstance());
        responseObserver.onCompleted();
    }

    @Override
    public void create(RpcCreateWorkTreeRequest request, StreamObserver<RpcCreateWorkTreeResponse> responseObserver) {
        WorkTree wt = workTreeManager.create(buildCreateWorkTreeParameters(request));

        RpcCreateWorkTreeResponse workTree = RpcCreateWorkTreeResponse.newBuilder()
                .setExpiry(wt.getExpiry().toEpochMilli())
                .setWorkTree(wt.getId())
                .build();

        responseObserver.onNext(workTree);
        responseObserver.onCompleted();
    }

    @Override
    public void mkdir(RpcWorkTreeMkdirRequest request, StreamObserver<RpcWorkTreeMkdirResponse> responseObserver) {
        WorkTree workTree = workTreeManager.getById(request.getWorkTree());
        workTreeManager.mkdir(workTree, request.getPath().toStringUtf8());

        responseObserver.onNext(RpcWorkTreeMkdirResponse.getDefaultInstance());
        responseObserver.onCompleted();
    }

    @Override
    public StreamObserver<RpcPublishWorkTreeFragment> publish(StreamObserver<RpcPublishWorkTreeResponseFragment> responseObserver) {
        return new PublishWorkTreeRequestObserver(responseObserver, quarantineRegistry, workTreeManager);
    }

    @Override
    public StreamObserver<RpcWriteWorkTreeFileFragment> writeFile(StreamObserver<RpcWriteWorkTreeFileResponse> responseObserver) {
        return new WorkTreeWriteFileRequestObserver(responseObserver, workTreeManager);
    }

    private CreateWorkTreeParameters buildCreateWorkTreeParameters(RpcCreateWorkTreeRequest request) {
        Repository repository = repositoryManager.getById(request.getRepository());
        return new CreateWorkTreeParameters.Builder(repository, request.getCommitish().toStringUtf8(), Duration.ofSeconds(request.getExpiry()))
                .alternates(getAlternates(request))
                .pristine(request.getCheckoutType() == RpcCreateWorkTreeRequest.CheckoutType.SPARSE)
                .build();
    }

    private Set<Repository> getAlternates(RpcCreateWorkTreeRequest request) {
        return request.getAlternatesList()
                .stream()
                .map(repositoryManager::getById)
                .collect(Collectors.toSet());
    }
}
