package com.atlassian.bitbucket.scm.pull;

import com.atlassian.bitbucket.content.DiffContentCallback;

import javax.annotation.Nonnull;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Describes the commits selected for a pull request's effective diff.
 * <p>
 * When a pull request is created, the branches to merge from and to are chosen. This represents the pull request's
 * actual diff. However, it is up to the underlying SCM to determine how the branch diff is constructed by choosing
 * the commits that will be used to produce the <i>effective</i> diff, which is the diff that will be streamed by the
 * {@link ScmPullRequestCommandFactory#diff(PullRequestDiffCommandParameters, DiffContentCallback)
 * command factory}.
 * <p>
 * How the commits are selected is left entirely to the SCM. Some possibilities are:
 * <ul>
 *     <li>A diff between the incoming branch and the common ancestor shared with the target branch</li>
 *     <li>A diff between the incoming branch and a theoretical merge commit</li>
 * </ul>
 * The differences between these two theoretical diffs are likely to be subtle in most pull requests, but may be very
 * obvious in others. {@link ScmPullRequestCommandFactory#effectiveDiff() command factory} implementors are encouraged
 * to provide documentation as to how the effective diff is chosen for that implementation to aid plugin developers in
 * understanding the diff that will be generated.
 */
public class PullRequestEffectiveDiff {

    private final String sinceId;
    private final String untilId;

    public PullRequestEffectiveDiff(@Nonnull String untilId, @Nonnull String sinceId) {
        this.sinceId = checkNotNull(sinceId, "sinceId");
        this.untilId = checkNotNull(untilId, "untilId");
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (o instanceof PullRequestEffectiveDiff) {
            PullRequestEffectiveDiff d = (PullRequestEffectiveDiff) o;

            return sinceId.equals(d.sinceId) && untilId.equals(d.untilId);
        }
        return false;
    }

    /**
     * Retrieves the commit which will serve as the ancestor revision when generating a pull request's diff. It is
     * <i>strongly</i> encouraged that this be an actual ancestor of the {@link #getUntilId() "until"} commit, as
     * arbitrary diffs between unrelated commits may produce useless, noisy diffs.
     *
     * @return the ancestor commit in the diff
     */
    @Nonnull
    public String getSinceId() {
        return sinceId;
    }

    /**
     * Retrieves the commit which will serve as the tip revision when generating a pull request's diff. The diff
     * will show the changes from the {@link #getSinceId() "since"} commit up to and including this commit.
     *
     * @return the tip commit in the diff
     */
    @Nonnull
    public String getUntilId() {
        return untilId;
    }

    @Override
    public int hashCode() {
        int result = 7;
        result = 31 * result + sinceId.hashCode();
        result = 31 * result + untilId.hashCode();

        return result;
    }
}
