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

import com.atlassian.bitbucket.mesh.rpc.v1.RpcTag;
import com.google.common.base.MoreObjects;
import com.google.protobuf.ByteString;

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.defaultString;
import static org.apache.commons.lang3.StringUtils.trimToNull;

/**
 * A {@link Ref} denoting a tag in the repository.
 */
public class Tag extends AbstractRef {

    private final String hash;

    protected Tag(@Nonnull AbstractGitTagBuilder<?, ?> builder) {
        super(builder);

        hash = builder.hash;
    }

    @Nonnull
    public static Tag fromTag(@Nonnull RpcTag tag) {
        requireNonNull(tag, "tag");
        return new Tag.Builder()
                .id(tag.getId().toStringUtf8())
                .hash(trimToNull(tag.getHash()))
                .latestCommit(tag.getLatestCommit())
                .build();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Tag that = (Tag) o;
        return super.equals(o) && Objects.equals(hash, that.hash);
    }

    /**
     * The hash for the tag itself. Where {@link #getLatestCommit()} returns the hash of the tagged element, this
     * property provides the hash for the tag.
     *
     * @return the tag's hash, which is {@code null} for lightweight tags
     */
    @Nullable
    public String getHash() {
        return hash;
    }

    @Nonnull
    @Override
    public String getObjectId() {
        return MoreObjects.firstNonNull(hash, getLatestCommit());
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), hash);
    }

    @Override
    public String toString() {
        return "Tag{id='" + getId() + "', latestCommit='" + getLatestCommit() + "', hash='" + hash + "'}";
    }

    @Nonnull
    public RpcTag toTag() {
        return RpcTag.newBuilder()
                .setId(ByteString.copyFromUtf8(getId()))
                .setHash(defaultString(hash))
                .setLatestCommit(getLatestCommit())
                .build();
    }

    public static class Builder extends AbstractGitTagBuilder<Tag, Builder> {

        @Nonnull
        @Override
        public Tag build() {
            return new Tag(self());
        }

        @Nonnull
        @Override
        protected Builder self() {
            return this;
        }
    }

    protected abstract static class AbstractGitTagBuilder<T extends Tag, B extends AbstractGitTagBuilder<T, B>>
            extends AbstractBuilder<T, B> {

        private String hash;

        public AbstractGitTagBuilder() {
            super(RefType.TAG);
        }

        @Nonnull
        public B hash(@Nullable String hash) {
            this.hash = hash;
            return self();
        }
    }
}
