package com.atlassian.stash.internal.pull.comment.drift;

import com.atlassian.bitbucket.util.Chainable;
import com.atlassian.stash.internal.pull.InternalPullRequest;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Objects;

import static com.atlassian.stash.internal.HibernateUtils.initialize;
import static com.atlassian.stash.internal.HibernateUtils.initializeList;

/**
 * Hibernate implementation of {@link CommentDriftRequestDao}. Note that this class does not extend 
 * {@link com.atlassian.stash.internal.AbstractHibernateDao} because we explicitly do no want to support a paging API
 * for working with {@link InternalDriftRequest} instances.
 * 
 * @since 1.3
 */
@Repository("driftRequestDao")
public class HibernateCommentDriftRequestDao implements CommentDriftRequestDao {

    private final SessionFactory sessionFactory;

    @Autowired
    public HibernateCommentDriftRequestDao(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    @Nonnull
    public InternalDriftRequest create(@Nonnull InternalDriftRequest request) {
        session().save(request);
        return request;
    }

    @Override
    public void deleteAll(@Nonnull List<InternalDriftRequest> requests) {
        Chainable.chain(requests)
                .transform(InternalDriftRequest::getId)
                .filter(Objects::nonNull)
                .partition(100)
                .filter(ids -> !ids.isEmpty())
                .forEach(ids -> session().createQuery("delete from InternalDriftRequest where id in (:ids)")
                        .setParameterList("ids", ids)
                        .executeUpdate());
    }
    
    @Override
    @Nonnull
    @SuppressWarnings("unchecked")
    public List<InternalDriftRequest> findAll() {
        return initializeList(session().createQuery("from InternalDriftRequest order by id asc").list());
    }

    @Override
    @Nonnull
    @SuppressWarnings("unchecked")
    public List<InternalDriftRequest> findByGlobalId(long globalId) {
        return initializeList(session()
                .createQuery("from InternalDriftRequest r where r.pullRequest.id = :globalId order by id asc")
                .setLong("globalId", globalId)
                .list());
    }

    @Nonnull
    @Override
    @SuppressWarnings("unchecked")
    public List<InternalPullRequest> findPendingPullRequests(int maxAttempts) {
        return initializeList(session()
                .createQuery("select p from InternalPullRequest p where p.id in (" +
                        "select distinct r.pullRequest.id from InternalDriftRequest r where r.attempts <= :maxAttempts)")
                .setParameter("maxAttempts", maxAttempts)
                .list());
    }

    @Override
    public InternalDriftRequest getById(@Nonnull Long id) {
        InternalDriftRequest request = (InternalDriftRequest) session().get(InternalDriftRequest.class, id);

        return initialize(request);
    }

    @Override
    public void updateAttempts(@Nonnull InternalDriftRequest request, int attempts) {
        session().createQuery("update InternalDriftRequest set attempts = :attempts where id = :id")
                .setParameter("attempts", attempts)
                .setParameter("id", request.getId())
                .executeUpdate();
        session().evict(request);
    }

    private Session session() {
        return sessionFactory.getCurrentSession();
    }
}
