package com.atlassian.stash.internal.repository.sync.command;

import com.atlassian.bitbucket.repository.*;
import com.atlassian.bitbucket.scm.SimpleCommand;
import com.atlassian.bitbucket.scm.git.GitAgent;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.GitUtils;
import com.atlassian.bitbucket.scm.git.command.updateref.GitUpdateRef;
import com.atlassian.bitbucket.scm.git.command.updateref.GitUpdateRefBuilderSupport;
import com.atlassian.bitbucket.user.ApplicationUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @since 2.6
 */
public class DiscardRefCommand extends SimpleCommand<RefChange> {

    private static final Logger log = LoggerFactory.getLogger(DiscardRefCommand.class);

    private final GitAgent agent;
    private final GitCommandBuilderFactory builderFactory;
    private final String refId;
    private final Repository repository;
    private final Repository upstream;
    private final ApplicationUser user;

    public DiscardRefCommand(GitAgent agent, GitCommandBuilderFactory builderFactory,
                             DiscardRefCommandParameters parameters) {
        this.agent = agent;
        this.builderFactory = builderFactory;

        refId = parameters.getRefId();
        repository = parameters.getRepository();
        upstream = parameters.getUpstream();
        user = parameters.getUser();
    }

    @Override
    public RefChange call() {
        Branch toBranch = agent.resolveBranch(repository, refId, true);
        if (toBranch == null) { //If toBranch is null, to make it here fromBranch can't possibly be
            log.debug("{}: {} has already been deleted", repository, refId);

            return null;
        }
        Branch fromBranch = agent.resolveBranch(upstream, refId, true);

        SimpleRefChange.Builder changeBuilder = new SimpleRefChange.Builder()
                .from(toBranch);
        GitUpdateRef updateRef = builderFactory.builder(repository).updateRef();
        GitUpdateRefBuilderSupport<?> commandBuilder;
        if (fromBranch == null) {
            log.debug("{}: Deleting {}, which has been deleted upstream in {}", repository, refId, upstream);
            changeBuilder.toHash(GitUtils.NULL_SHA1)
                    .type(RefChangeType.DELETE);

            commandBuilder = updateRef.delete(refId);
        } else {
            changeBuilder.to(fromBranch)
                    .type(RefChangeType.UPDATE);

            commandBuilder = updateRef.set(refId, fromBranch.getLatestCommit());
        }
        //Any exception thrown is intentionally propagated back to the caller
        commandBuilder.author(user)
                .build()
                .call();

        return changeBuilder.build();
    }
}
