package com.atlassian.stash.internal.repository;

import com.atlassian.stash.internal.activity.ActivityType;
import com.atlassian.bitbucket.repository.RefChange;
import com.atlassian.bitbucket.repository.RepositoryActivityVisitor;
import com.atlassian.bitbucket.repository.RepositoryPushActivity;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.annotation.Nonnull;
import javax.persistence.*;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;

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

/**
 * @since 2.11
 */
@DiscriminatorValue(ActivityType.REPOSITORY_PUSH_ACTIVITY_DISCRIMINATOR)
@Entity
@PrimaryKeyJoinColumn(name = "activity_id", referencedColumnName = "activity_id",
        foreignKey = @ForeignKey(name = "fk_sta_repo_push_act_id"))
@Table(name = InternalRepositoryPushActivity.TABLE)
public class InternalRepositoryPushActivity extends InternalRepositoryActivity implements RepositoryPushActivity {

    public static final String TABLE = "sta_repo_push_activity";

    @CollectionTable(name = "sta_repo_push_ref", joinColumns = @JoinColumn(name = "activity_id", nullable = false))
    @ElementCollection(targetClass = InternalRefChange.class)
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    private final Set<RefChange> refChanges;

    protected InternalRepositoryPushActivity() {
        refChanges = Sets.newHashSet();
    }

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

        refChanges = ImmutableSet.copyOf(builder.refChanges);
    }

    @Override
    public <T> T accept(@Nonnull RepositoryActivityVisitor<T> visitor) {
        return visitor.visit(this);
    }

    @Nonnull
    @Override
    public Collection<RefChange> getRefChanges() {
        return refChanges;
    }

    public static class Builder extends AbstractRepositoryActivityBuilder<Builder, InternalRepositoryPushActivity> {

        private final Set<RefChange> refChanges = Sets.newHashSet();

        public Builder(@Nonnull InternalRepository repository) {
            super(repository);
        }

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

            refChanges.addAll(activity.getRefChanges());
        }

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

        @Nonnull
        public Builder refChanges(@Nonnull Iterable<RefChange> value) {
            checkNotNull(value, "refChanges");
            //Due to misbehaving plugins, it's possible some of the RefChanges in the collection will not be able
            //to be persisted because they do not honor the RefChange contract. This addIf filters out any changes
            //which cannot be transformed
            addIf(Objects::nonNull, refChanges, Iterables.transform(value, InternalRefChange.TRANSFORM_STRICT));

            return self();
        }

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