package com.atlassian.bitbucket.content;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitService;
import com.atlassian.bitbucket.property.PropertySupport;
import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.pull.PullRequestChangesRequest;
import com.atlassian.bitbucket.pull.PullRequestService;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * Describes a single change made by a {@link Commit commit}.
 */
public interface Change extends PropertySupport {

    /**
     * Retrieves conflict information, if any, related to this change.
     * <p>
     * When {@link PullRequestService#streamChanges(
     * PullRequestChangesRequest, ChangeCallback) streaming changes} for a
     * {@link PullRequest pull request}, it is possible for some changes to be decorated with
     * conflict information. Other sources of change information, such as the
     * {@link CommitService CommitService}, will never include conflicts on any of the
     * changes they return.
     * <p>
     * When conflict information is present, the {@link ConflictChange conflicting changes} describe the changes that
     * were made on each side of the merge. <i>Details provided with this change will not be accurate when conflict
     * information is present.</i> Instead, this change will describe the way the conflict was handled by the system
     * to allow the merge to be committed.
     *
     * @return conflict information related to this change, or {@code null} if there is no conflict
     */
    @Nullable
    Conflict getConflict();

    /**
     * An identifier for the content which was changed.
     * <p>
     * For {@link ChangeType#DELETE deletions}, the returned content ID is for the content prior to deletion, since
     * there is no identifier available afterward. For all other {@link ChangeType types}, the identifier is for the
     * content after the change.
     * <p>
     * Some SCM implementations, such as Mercurial, do not provide content IDs. When working with repositories for
     * such SCMs, this value will be {@code null}.
     *
     * @return a content identifier
     */
    @Nullable
    String getContentId();

    /**
     * The executable permission for the file <i>after</i> the {@link #getType() change}.
     * <p>
     * If the file was {@link ChangeType#DELETE deleted} this value will be {@code null}, since a deleted file is
     * neither executable nor non-executable. Otherwise, it will be {@code true} or {@code false} according to the
     * permissions on the file.
     *
     * @return {@code true} or {@code false} for any change other than a {@link ChangeType#DELETE deletion};
     *         {@code null} for deleted files
     */
    @Nullable
    Boolean getExecutable();

    /**
     * An identifier from which the content was changed.
     *
     * Some SCM implementations, such as Mercurial, do not provide content IDs. When working with repositories for
     * such SCMs, this value will be {@code null}.
     *
     * @return a content identifier
     */
    @Nullable
    String getFromContentId();

    /**
     * The type of the file node, which is most likely to be a {@link ContentTreeNode.Type#FILE file} or possibly a
     * {@link ContentTreeNode.Type#SUBMODULE submodule}. A {@link ContentTreeNode.Type#DIRECTORY directory} type
     * is never returned as they are transient and can be recreated from the {@link #getPath() path}.
     *
     * @return the node type
     */
    @Nonnull
    ContentTreeNode.Type getNodeType();

    /**
     * The path to the changed content.
     * <ul>
     * <li>For {@link ChangeType#COPY copies}, this is the path of the content that was created</li>
     * <li>For {@link ChangeType#DELETE deletions}, this is the path of the content before it was deleted</li>
     * <li>For {@link ChangeType#MOVE moves}, this is the path of content after the move</li>
     * <li>For all other {@link ChangeType types}, this is the path of the content to which the change occurred</li>
     * </ul>
     *
     * @return the changed path
     */
    @Nonnull
    Path getPath();

    /**
     * The percentage of content that matches before and after a change. This is useful, for example, in determining
     * the likelihood that an identified {@link ChangeType#COPY copy} or {@link ChangeType#MOVE move} is correct.
     * <p>
     * Note: This feature may not be supported by all SCM implementations. Where it is not supported, it will always
     * return {@code -1}.
     *
     * @return a percentage, if the change is scored, or {@code -1} if it is not
     */
    int getPercentUnchanged();

    /**
     * The executable permission for the file <i>before</i> the {@link #getType() change}.
     * <p>
     * If the file was {@link ChangeType#ADD created} this value will be {@code null}, since the file was neither
     * executable nor non-executable before it was created. Otherwise, it will be {@code true} or {@code false}
     * according to the permissions on the file.
     *
     * @return {@code true} or {@code false} for any change other than a {@link ChangeType#ADD creation};
     *         {@code null} for newly-created files
     */
    @Nullable
    Boolean getSrcExecutable();

    /**
     * The path at which the changed content originated.
     * <ul>
     * <li>For {@link ChangeType#COPY copies}, this is the path to the content that was copied</li>
     * <li>For {@link ChangeType#MOVE moves}, this is the path to the content before it was moved</li>
     * <li>For all other {@link ChangeType types}, this value is not defined</li>
     * </ul>
     *
     * @return the originating path, if any
     */
    @Nullable
    Path getSrcPath();

    /**
     * The type of change that was applied.
     *
     * @return the change type
     */
    @Nonnull
    ChangeType getType();
}
