package com.atlassian.bitbucket.repository.sync;

/**
 * Enumerates the possible reasons why a given {@link RejectedRef ref} may not be automatically synchronized with its
 * upstream and, for each state, describes the {@link RefSyncAction actions} which may be applied to synchronize the
 * ref manually.
 */
public enum RejectedRefState {

    /**
     * Indicates the ref could not be synchronized because it is <i>strictly ahead</i> of its upstream. This means
     * the ref already includes every commit reachable from its upstream ref, plus more.
     * <p>
     * When the ref is ahead of its upstream, performing a {@link RefSyncAction#MERGE merge} will fail because there
     * are no new commits to merge in. Only {@link RefSyncAction#DISCARD overwriting} the ref can synchronize it
     * with its upstream.
     */
    AHEAD(1),
    /**
     * Indicates the ref could not be synchronized because it has <i>diverged</i> from its upstream. This means the
     * ref and its upstream <i>both</i> include at least one commit which is not reachable from the other.
     * <p>
     * When the ref has diverged from its upstream, performing a {@link RefSyncAction#MERGE merge} can synchronize
     * the ref with its upstream by adding all the commits reachable from the ref's upstream but not from itself.
     * The refs can, of course, also be synchronized by {@link RefSyncAction#DISCARD overwriting} the ref.
     */
    DIVERGED(2),
    /**
     * Indicates the ref could not be synchronized because it was {@link #AHEAD ahead of} or {@link #DIVERGED diverged
     * from} its upstream and its upstream has been deleted. When an upstream ref is deleted, downstream refs are also
     * deleted if, <i>and only if</i>, they reference the same commit as upstream. If they do not, meaning they are
     * {@link #AHEAD ahead} or {@link #DIVERGED diverged}, they cannot be removed automatically (as work may be lost).
     * They are marked as orphaned instead.
     * <p>
     * When a ref has been orphaned, {@link RefSyncAction#DISCARD "overwriting"} the ref will delete it. No other
     * synchronization actions make sense for an orphaned ref.
     */
    ORPHANED(3);

    private final int id;

    RejectedRefState(int id) {
        this.id = id;
    }

    public static RejectedRefState fromId(int id) {
        for (RejectedRefState value : values()) {
            if (value.getId() == id) {
                return value;
            }
        }
        throw new IllegalArgumentException("No RejectedRefState is associated with ID [" + id + "]");
    }

    public int getId() {
        return id;
    }
}
