package com.atlassian.stash.internal.pull;

import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.pull.PullRequestAction;
import com.atlassian.bitbucket.pull.PullRequestActivity;
import com.atlassian.bitbucket.pull.PullRequestActivityVisitor;
import com.atlassian.stash.internal.HibernateUtils;
import com.atlassian.stash.internal.activity.ActivityType;
import com.atlassian.stash.internal.activity.InternalActivity;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;

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

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

/**
 * Describes common attributes for all {@link PullRequest pull request} activities. For some
 * {@link PullRequestAction actions}, the information described here may be all that is available; see the action
 * documentation for details about the specific activity each action creates.
 *
 * @since 1.3
 */
@DiscriminatorValue(ActivityType.PULL_REQUEST_ACTIVITY_DISCRIMINATOR)
@Entity
@PrimaryKeyJoinColumn(name = "activity_id", referencedColumnName = "id",
        foreignKey = @ForeignKey(name = "fk_sta_pr_activity_id"))
@Table(name = InternalPullRequestActivity.TABLE, indexes = {
        @Index(name = "idx_sta_pr_activity", columnList = "pr_id, pr_action")
})
public class InternalPullRequestActivity extends InternalActivity implements PullRequestActivity {

    public static final String TABLE = "sta_pr_activity";

    @Column(name = "pr_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.pull.PullRequestAction")
    })
    private final PullRequestAction action;

    @JoinColumn(name = "pr_id", nullable = false, foreignKey = @ForeignKey(name = "fk_sta_pr_activity_pr"), updatable = false)
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private final InternalPullRequest pullRequest;

    protected InternalPullRequestActivity() {
        action = null;
        pullRequest = null;
    }

    protected InternalPullRequestActivity(AbstractPullRequestActivityBuilder<?, ?> builder) {
        super(builder);

        action = builder.action;
        pullRequest = builder.pullRequest;
    }

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

    @Nonnull
    @Override
    public PullRequestAction getAction() {
        return action;
    }

    @Nonnull
    @Override
    public InternalPullRequest getPullRequest() {
        return pullRequest;
    }

    @Override
    public void initialize() {
        super.initialize();
        HibernateUtils.initialize(getPullRequest());
    }

    @Nonnull
    @Override
    protected MoreObjects.ToStringHelper toStringHelper() {
        return super.toStringHelper()
                .add("action", action)
                .add("pullRequest.id", pullRequest != null ? pullRequest.getId() : null);
    }

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

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

        public Builder(@Nonnull InternalPullRequestActivity activity) {
            super(activity);
        }

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

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

    protected abstract static class AbstractPullRequestActivityBuilder<B extends AbstractPullRequestActivityBuilder<B, A>, A extends InternalPullRequestActivity>
            extends AbstractActivityBuilder<B, A> {

        private final InternalPullRequest pullRequest;

        private PullRequestAction action;

        protected AbstractPullRequestActivityBuilder(@Nonnull InternalPullRequest pullRequest) {
            this.pullRequest = checkNotNull(pullRequest, "pullRequest");
        }

        protected AbstractPullRequestActivityBuilder(@Nonnull A activity) {
            super(activity);

            action = activity.getAction();
            pullRequest = activity.getPullRequest();
        }

        @Nonnull
        public B action(PullRequestAction value) {
            action = value;

            return self();
        }
    }
}
