package ru.simplex_software.wicket_springdata;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Persistable;
import org.springframework.data.repository.CrudRepository;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;

/**
 * Находит подкласс репозитория.
 */
public class RepositoryFinder<T extends Persistable<K>, K extends Serializable> implements ApplicationContextAware {
    private static final Logger LOG = LoggerFactory.getLogger(RepositoryFinder.class);

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * Находит репизоторий по типу Persistable.
     *
     * @param tClass класс Persistable.
     * @return первый CrudRepository для класса.
     */
    public CrudRepository<T, K> findRepository(Class<T> tClass) {
        final Collection<CrudRepository> repositoryCollection = applicationContext.getBeansOfType(CrudRepository.class).values();

        for (CrudRepository repository : repositoryCollection) {
            final Class[] interfaces = repository.getClass().getInterfaces();
            for (Class interfaze : interfaces) {
                for (Type pt : interfaze.getGenericInterfaces()) {
                    if (!(pt instanceof ParameterizedType)) {
                        continue;
                    }
                    final Type[] actualTypeArguments = ((ParameterizedType) pt).getActualTypeArguments();
                    if (Arrays.asList(actualTypeArguments).contains(tClass)) {
                        return repository;
                    }
                }
            }
        }
        LOG.warn("repository not found. PersistentEntity={}", tClass.getName());
        return null;
    }

    /**
     * Находит репизоторий по типу репозитория Persistable.
     *
     * @param tClass класс Persistable.
     * @return CrudRepository для класса.
     */
    public CrudRepository<T, K> findRepositoryByClass(Class<CrudRepository<T, K>> tClass) {
        final Collection<CrudRepository<T, K>> repositoryCollection = applicationContext.getBeansOfType(tClass).values();
        return repositoryCollection.iterator().next();
    }
}