package com.tools.common.dynamicquery;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.List;

/**
 * 查询的实现类
 * 爪哇笔记 https://blog.52itstyle.vip
 * @author 小柒2012
 */
@Repository
public class DynamicQueryImpl implements DynamicQuery {

	/**
	 * @PersistenceContext(type = PersistenceContextType.EXTENDED)
	 * 默认是 PersistenceContextType.TRANSACTION 开启只读事物
	 */
	@PersistenceContext
	private EntityManager em;

	public EntityManager getEntityManager() {
		return em;
	}

	@Override
	public void save(Object entity) {
		em.persist(entity);
	}

	@Override
	public void update(Object entity) {
		em.merge(entity);
	}

	private Query createNativeQuery(String sql, Object... params) {
		Query q = em.createNativeQuery(sql);
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		}
		return q;
	}

	@Override
	public int nativeExecuteUpdate(String nativeSql, Object... params) {
		return createNativeQuery(nativeSql, params).executeUpdate();
	}

	@Override
	public <T> T nativeQuerySingleResult(Class<T> resultClass, String nativeSql, Object... params) {
		Query q = createNativeQuery(resultClass, nativeSql, params);
		List<T> list = q.getResultList();
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}


	private <T> Query createNativeQuery(Class<T> resultClass, String sql, Object... params) {
		Query q;
		if (resultClass == null) {
			q = em.createNativeQuery(sql);
		} else {
			q = em.createNativeQuery(sql, resultClass);
		}
		if(params!=null){
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i + 1, params[i]);
			}
		}
		return q;
	}

	@Override
	public <T> List<T> query(String nativeSql, Object... params) {
		Query q = createNativeQuery(null, nativeSql, params);
		return q.getResultList();
	}

    @Override
    public <T> List<T> query(Class<T> resultClass, String nativeSql, Object... params) {
        Query q = createNativeQuery(resultClass, nativeSql, params);
        return q.getResultList();
    }
	@Override
	public Long nativeQueryCount(String nativeSql, Object... params) {
		nativeSql = StringUtils.substringBefore(nativeSql, "order by");
		Object count = createNativeQuery(nativeSql, params).getSingleResult();
		return ((Number) count).longValue();
	}
	@Override
	public <T> Page<T> nativeQuery(Class<T> resultClass, Pageable pageable, String nativeSql, Object... params) {
		List<T> rows = nativeQueryPagingList(resultClass, pageable, nativeSql, params);
		Long total = nativeQueryCount(nativeSql, params);
		return new PageImpl<>(rows, pageable, total);
	}
	@Override
	public <T> List<T> nativeQueryPagingList(Class<T> resultClass, Pageable pageable, String nativeSql,
											 Object... params) {
		Integer pageNumber = pageable.getPageNumber();
		Integer pageSize = pageable.getPageSize();
		Integer startPosition = pageNumber * pageSize;
		return createNativeQuery(resultClass, nativeSql, params)
                .setFirstResult(startPosition).setMaxResults(pageSize)
				.getResultList();
	}

    @Override
    public <T> T nativeQueryMap(String nativeSql, Object... params) {
        Query q = createNativeQuery(nativeSql, params);
        q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<T> list = q.getResultList();
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public <T> T nativeQueryModel(Class<T> resultClass, String nativeSql, Object... params) {
        Query q = createNativeQuery(nativeSql, params);
        q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(resultClass));
        List<T> list = q.getResultList();
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public <T> List<T> nativeQueryListModel(Class<T> resultClass,
                                            String nativeSql, Object... params) {
        Query q = createNativeQuery(nativeSql, params);
        q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(resultClass));
        return q.getResultList();
    }

    @Override
    public <T> List<T> nativeQueryPagingListModel(Class<T> resultClass, Pageable pageable, String nativeSql, Object... params) {
        Integer pageNumber = pageable.getPageNumber();
        Integer pageSize = pageable.getPageSize();
        Integer startPosition = pageNumber * pageSize;
	    Query q = createNativeQuery(nativeSql, params)
                .setFirstResult(startPosition).setMaxResults(pageSize);
        q.unwrap(NativeQueryImpl.class)
                .setResultTransformer(Transformers.aliasToBean(resultClass));
        return q.getResultList();
    }

    @Override
    public <T> List<T> nativeQueryListMap(String nativeSql, Object... params) {
        Query q = createNativeQuery(nativeSql, params);
        q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        return q.getResultList();
    }
}

