package com.atlassian.stash.internal.pull;

import com.atlassian.bitbucket.comment.CommentAction;
import com.atlassian.bitbucket.content.Diff;
import com.atlassian.bitbucket.pull.PullRequestAction;
import com.atlassian.bitbucket.pull.PullRequestActivityVisitor;
import com.atlassian.bitbucket.pull.PullRequestCommentActivity;
import com.atlassian.stash.internal.HibernateUtils;
import com.atlassian.stash.internal.activity.ActivityType;
import com.atlassian.stash.internal.comment.InternalComment;
import com.google.common.base.MoreObjects;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.*;

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

/**
 * @since 1.3
 */
@DiscriminatorValue(ActivityType.PULL_REQUEST_COMMENT_ACTIVITY_DISCRIMINATOR)
@Entity
@PrimaryKeyJoinColumn(name = "activity_id", referencedColumnName = "activity_id",
        foreignKey = @ForeignKey(name = "fk_sta_pr_com_act_id"))
@Table(name = InternalPullRequestCommentActivity.TABLE, indexes = {
        @Index(name = "idx_sta_pr_com_act_action", columnList = "comment_action"),
        @Index(name = "idx_sta_pr_com_act_anchor", columnList = "anchor_id"),
        @Index(name = "idx_sta_pr_com_act_comment", columnList = "comment_id")
})
public class InternalPullRequestCommentActivity
        extends InternalPullRequestActivity
        implements PullRequestCommentActivity {

    public static final String TABLE = "sta_pr_comment_activity";

    @JoinColumn(name = "comment_id", nullable = false, foreignKey = @ForeignKey(name = "fk_sta_pr_com_act_comment"), updatable = false)
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private final InternalComment comment;

    @Column(name = "comment_action", nullable = false, updatable = false)
    @org.hibernate.annotations.Type(type = "com.atlassian.hibernate.extras.type.GenericEnumUserType", parameters = {
            @org.hibernate.annotations.Parameter(name = "enumClass", value = "com.atlassian.bitbucket.comment.CommentAction")
    })
    private final CommentAction commentAction;

    @JoinColumn(name = "anchor_id", foreignKey = @ForeignKey(name = "fk_sta_pr_com_act_anchor"), updatable = false)
    @ManyToOne(fetch = FetchType.LAZY)
    private final InternalPullRequestDiffCommentAnchor commentAnchor;

    @Transient
    private Diff diff;

    @SuppressWarnings("unused") //Required for Hibernate
    protected InternalPullRequestCommentActivity() {
        comment = null;
        commentAction = null;
        commentAnchor = null;
    }

    private InternalPullRequestCommentActivity(Builder builder) {
        super(builder);

        comment = builder.comment;
        commentAction = builder.commentAction;
        commentAnchor = builder.commentAnchor;
    }

    @Override
    public void accept(@Nonnull PullRequestActivityVisitor visitor) {
        visitor.visit(this);
    }

    @Nonnull
    @Override
    public InternalComment getComment() {
        return comment;
    }

    @Nonnull
    @Override
    public CommentAction getCommentAction() {
        return commentAction;
    }

    @Override
    public InternalPullRequestDiffCommentAnchor getCommentAnchor() {
        return commentAnchor;
    }

    @Override
    public Diff getDiff() {
        return diff;
    }

    @Override
    public void initialize() {
        super.initialize();

        HibernateUtils.initialize(getComment());
        HibernateUtils.initialize(getCommentAnchor());
    }

    public void setDiff(Diff value) {
        if (diff != null) {
            throw new IllegalStateException("You cannot set the diff after it's been initialized");
        }
        diff = value;
    }

    @Nonnull
    @Override
    protected MoreObjects.ToStringHelper toStringHelper() {
        return super.toStringHelper()
                .add("commentAction", commentAction)
                .add("comment", comment);
    }

    public static class Builder extends AbstractPullRequestActivityBuilder<Builder, InternalPullRequestCommentActivity> {

        private InternalComment comment;
        private CommentAction commentAction;
        private InternalPullRequestDiffCommentAnchor commentAnchor;

        public Builder(@Nonnull InternalPullRequest pullRequest) {
            super(pullRequest);

            action(PullRequestAction.COMMENTED);
        }

        public Builder(@Nonnull InternalPullRequestCommentActivity activity) {
            super(activity);

            comment = activity.getComment();
            commentAction = activity.getCommentAction();
            commentAnchor = activity.getCommentAnchor();
        }

        @Nonnull
        @Override
        public InternalPullRequestCommentActivity build() {
            return new InternalPullRequestCommentActivity(this);
        }

        @Nonnull
        public Builder comment(@Nonnull InternalComment value) {
            comment = checkNotNull(value, "comment");

            return self();
        }

        @Nonnull
        public Builder commentAction(@Nonnull CommentAction value) {
            commentAction = checkNotNull(value, "commentAction");

            return self();
        }

        @Nonnull
        public Builder commentAnchor(@Nullable InternalPullRequestDiffCommentAnchor value) {
            commentAnchor = value;

            return self();
        }

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