package com.atlassian.stash.internal.crowd;

import com.atlassian.crowd.dao.CriteriaFactory;
import com.atlassian.crowd.util.persistence.hibernate.batch.AbstractBatchFinder;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;

import java.util.Collection;

import static com.atlassian.crowd.embedded.impl.IdentifierUtils.toLowerCase;

/**
 * {@link com.atlassian.crowd.util.persistence.hibernate.batch.BatchFinder Batchfinder} implementation that runs the
 * batch find in the <i>current transaction</i>.
 * <p>
 * The <i>intention</i> when this class was created was for the batch find to run in a new transaction. However, the
 * {@code &#064;Transactional} annotation was applied to the wrong method and a separate transaction wasn't created.
 * Since that mistake was made, {@code Hibernate.initialize} calls have been added to the {@code HibernateGroupDao}
 * and {@code HibernateUserDao}, and those calls rely on the entities returned by the {@code BatchFinder} still being
 * attached to a Hibernate {@code Session}.
 * <p>
 * Long term, this class should be fixed to use a separate transaction as was always intended. However, that will
 * require reworking the initialization. For the short term, the DAOs have been updated to evict the entities from
 * the {@code Session} after they're initialized.
 * 
 * @since 1.3
 */
public class TransactionManagerBatchFinder extends AbstractBatchFinder {

    private final SessionFactory sessionFactory;

    public TransactionManagerBatchFinder(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    //@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) <- This won't be applied
    protected <E> Collection<E> processBatchFind(long directoryID, Collection<String> names, Class<E> persistentClass) {
        final Collection<String> lowercaseNames = Collections2.transform(names,
                new Function<String, String>() {
                    public String apply(final String from) {
                        return toLowerCase(from);
                    }
                }
        );

        //noinspection unchecked
        return CriteriaFactory.createCriteria(sessionFactory.getCurrentSession(), persistentClass)
                .add(Restrictions.eq("directory.id", directoryID))
                .add(Restrictions.in("lowerName", lowercaseNames))
                .list();
    }
}
