package com.wx.application.base;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.wx.application.constant.CONSTANT;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import static com.baomidou.mybatisplus.extension.toolkit.SqlHelper.retBool;

/*import org.springframework.beans.BeanUtils;*/


/**
 * @author zj
 * @description : service 基类
 * ---------------------------------
 * @since 2017-10-27
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public class BaseService<T extends BaseEntity<?>, M extends BaseMapper<T>> {

    @Autowired
    protected M simpleMapper;

    protected DynamicSpecifications<T> dynamicSpecifications = new DynamicSpecifications();

    /**
     * 读取一个对象
     *
     * @return
     */
    @Transactional(readOnly = true)
    public T queryUnique(Map map) {
        AbstractWrapper wrapper = dynamicSpecifications.bySearchFilter(SearchFilter.parse(map).values());
        wrapper = (AbstractWrapper) wrapper.last("limit 1");
        return (T) simpleMapper.selectOne(wrapper);
    }

    /**
     * 查询（根据ID 批量查询）
     *
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> queryBatchIds(List ids) {
        return simpleMapper.selectBatchIds(ids);
    }

    @Transactional(readOnly = true)
    public T getOne(Serializable id) {
        return simpleMapper.selectById(id);
    }

    /**
     * 分页查询
     *
     * @return
     */
    public IPage<T> queryPageSort(Map map, List<BaseQueryApplyOrderBy> sorts) {
        return queryPageAppender(map, null, sorts);
    }

    public IPage<T> queryPage(Map map) {
        return queryPageAppender(map, null, null);
    }

    /**
     * 支持累加的分页查询
     *
     * @param map
     * @param queryApplyList
     * @return
     */
    @Transactional(readOnly = true)
    public IPage<T> queryPageAppender(Map map, List<BaseQueryApply> queryApplyList, List<BaseQueryApplyOrderBy> orderBys) {
        Long pageNum = MapUtil.getLong(map, CONSTANT.pageNumStr);
        Long pageSize = MapUtil.getLong(map, CONSTANT.pageSizeStr);
        if (null == pageNum) {
            pageNum = CONSTANT.pageNum;
        }
        if (null == pageSize) {
            pageSize = CONSTANT.pageSize;
        }
        QueryWrapper<T> queryWrapper = dynamicSpecifications.bySearchFilter(SearchFilter.parse(map).values());
        if (CollectionUtil.isNotEmpty(queryApplyList)) {
            queryApplyList.forEach(apply -> queryWrapper.apply(apply.getApplySql(), apply.getValue().toArray()));
        }
        if (CollectionUtil.isNotEmpty(orderBys)) {
            orderBys.forEach(o -> {
                switch (o.getSort()) {
                    case ASC:
                        queryWrapper.orderByAsc(o.getSql());
                    case DESC:
                        queryWrapper.orderByDesc(o.getSql());
                    default:
                }

            });
        }
        return simpleMapper.selectPage(new Page(pageNum, pageSize), queryWrapper);
    }

    public Page toPage(Map map) {

        Long pageNum = MapUtil.getLong(map, CONSTANT.pageNumStr);
        Long pageSize = MapUtil.getLong(map, CONSTANT.pageSizeStr);
        if (null == pageNum) {
            pageNum = CONSTANT.pageNum;
        }
        if (null == pageSize) {
            pageSize = CONSTANT.pageSize;
        }

        return new Page(pageNum, pageSize);
    }

    /**
     * 查询全部
     *
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> queryList(Map map) {
        return simpleMapper.selectList(dynamicSpecifications.bySearchFilter(SearchFilter.parse(map).values()));
    }

    /**
     * 查询全部
     *
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> queryListAppender(Map map, List<BaseQueryApply> queryApplyList) {
        QueryWrapper<T> queryWrapper = dynamicSpecifications.bySearchFilter(SearchFilter.parse(map).values());
        if (CollectionUtil.isNotEmpty(queryApplyList)) {
            queryApplyList.forEach(apply -> queryWrapper.apply(apply.getApplySql(), apply.getValue().toArray()));
        }
        return simpleMapper.selectList(queryWrapper);

    }

    @Transactional
    public synchronized boolean remove(Serializable id) {
        return retBool(simpleMapper.deleteById(id));
    }

    @Transactional
    public synchronized boolean modify(T t) {
        if (null == t.getId()) {
            throw new RuntimeException("参数异常");
        }
        return retBool(simpleMapper.updateById(t));
    }

    @Transactional
    public synchronized T create(T t) {
        if (null != t.getId()) {
            throw new RuntimeException("参数异常");
        }
        simpleMapper.insert(t);
        return t;
    }

    @Transactional
    public synchronized boolean createOrUpdate(T t) {
        if (null == t.getId()) {
            return retBool(simpleMapper.insert(t));
        }
        return retBool(simpleMapper.updateById(t));
    }

    @Transactional
    public synchronized boolean removeBatch(List<Long> ids) {
        return retBool(simpleMapper.deleteBatchIds(ids));
    }
    
    /**
   	 * 批量插入
   	 * 
   	 * @throws DbException
   	 */
   	@Transactional
   	public boolean insertBatch(List<T> entityList) {
   		int batchSize = 20;
   		try (SqlSession batchSqlSession = sqlSessionBatch()) {
   			int size = entityList.size();
   			String sqlStatement = sqlStatementByXml(SqlMethod.INSERT_ONE.getMethod());
   			for (int i = 0; i < size; i++) {
   				batchSqlSession.insert(sqlStatement, entityList.get(i));
   				if (i >= 1 && i % batchSize == 0) {
   					batchSqlSession.flushStatements();
   				}
   			}
   			batchSqlSession.flushStatements();
   		} catch (Throwable e) {
   			e.printStackTrace();
   		}
   		return true;
   	}
       
       /**
   	 * 批量更新
   	 */
   	@Transactional
   	public boolean updateBatch(List<T> entityList) {
   		int batchSize = 20;
   		try (SqlSession batchSqlSession = sqlSessionBatch()) {
   			int size = entityList.size();
   			String sqlStatement = sqlStatementByXml("updateBatch");
   			for (int i = 0; i < size; i++) {
   				batchSqlSession.update(sqlStatement, entityList.get(i));
   				if (i >= 1 && i % batchSize == 0) {
   					batchSqlSession.flushStatements();
   				}
   			}
   			batchSqlSession.flushStatements();
   		} catch (Throwable e) {
   			e.printStackTrace();
   		}
   		return true;
   	}
       
       
       /**
        * 自定义批量插入
        * @param entityList
        * @param batchSize
        * @return
        * @throws Exception
        */

       @Transactional
       public boolean insertBatchByXml(List<T> entityList) {
       	int batchSize = 20;
   		try (SqlSession batchSqlSession = sqlSessionBatch()) {
   			int size = entityList.size();
   			String sqlStatement = sqlStatementByXml("insertBatch");
   			for (int i = 0; i < size; i++) {
   				batchSqlSession.insert(sqlStatement, entityList.get(i));
   				if (i >= 1 && i % batchSize == 0) {
   					batchSqlSession.flushStatements();
   				}
   			}
   			batchSqlSession.flushStatements();
   		} catch (Throwable e) {
   			e.printStackTrace();
   		}
   		return true;
   	}
       
       protected Class currentModelClass() {
   		return ReflectionKit.getSuperClassGenericType(getClass(), 0);
   	}

   	/**
   	 * 批量操作 SqlSession
   	 */
   	protected SqlSession sqlSessionBatch() {
   		return SqlHelper.sqlSessionBatch(currentModelClass());
   	}

   	/**
   	 * 获取SqlStatement
   	 */
   	protected String sqlStatementByXml(String method) {
   		return SqlHelper.table(currentModelClass()).getSqlStatement(method);
   	}
}
