package com.atlassian.bitbucket.mesh.git.hook;

import com.atlassian.bitbucket.mesh.execution.ExecutionContext;
import com.atlassian.bitbucket.mesh.repository.Repository;

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

import java.util.Objects;

import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

/**
 * Identifies the {@link ExecutionContext} in which the {@code git} process was started that triggered a git hook
 * callback.
 * <p>
 * There are two valid forms of identification:
 * <ol>
 *     <li>by {@link #getHookRequestId() hook request ID} - when we explicitly
 *         {@link GitHookService#registerRequest(Repository, GitHookHandler...) register} the process with the
 *         {@link GitHookService}</li>
 *      <li>by {@link #getExecutionContextId() execution context ID} <em>AND</em> {@link #getRepositoryId() repository ID} -
 *          otherwise</li>
 * </ol>
 */
public class HookContextId {

    private final String executionContextId;
    private final String hookRequestId;
    private final String repositoryId;

    private HookContextId(@Nullable String hookRequestId, @Nullable String executionContextId,
                          @Nullable String repositoryId) {
        this.executionContextId = executionContextId;
        this.hookRequestId = hookRequestId;
        this.repositoryId = repositoryId;
    }

    @Nonnull
    public static HookContextId ofExecutionContext(@Nonnull String executionContextId, @Nonnull String repositoryId) {
        return new HookContextId(null, requireNonNull(executionContextId, "executionContextId"),
                requireNonNull(repositoryId, "repositoryId"));
    }

    @Nonnull
    public static HookContextId ofHookRequestId(@Nonnull String hookRequestId) {
        return new HookContextId(requireNonNull(hookRequestId, "hookRequestId"), null, null);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        HookContextId contextId = (HookContextId) o;
        return Objects.equals(executionContextId, contextId.executionContextId) &&
                Objects.equals(hookRequestId, contextId.hookRequestId) &&
                Objects.equals(repositoryId, contextId.repositoryId);
    }

    @Nullable
    public String getExecutionContextId() {
        return executionContextId;
    }

    @Nullable
    public String getHookRequestId() {
        return hookRequestId;
    }

    @Nullable
    public String getRepositoryId() {
        return repositoryId;
    }

    public boolean hasHookRequestId() {
        return hookRequestId != null;
    }

    @Override
    public int hashCode() {
        return Objects.hash(executionContextId, hookRequestId, repositoryId);
    }

    @Override
    public String toString() {
        if (hookRequestId != null) {
            return "hook-id:" + hookRequestId;
        }
        if (isNotBlank(executionContextId)) {
            return "execution-ctx-id:" + executionContextId + (isBlank(repositoryId) ? "" : " " + repositoryId);
        }
        if (isNotBlank(repositoryId)) {
            return "repo:" + repositoryId;
        }
        return "unknown";
    }
}
