package com.goafanti.core.mybatis;

import com.goafanti.common.utils.BeanUtilsExt;
import com.goafanti.common.utils.LoggerUtils;
import com.goafanti.core.mybatis.page.MysqlDialect;
import com.goafanti.core.mybatis.page.Pagination;
import com.goafanti.core.mybatis.page.PagintionPlus;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SuppressWarnings({ "unchecked" })
public class BaseMybatisDao<T> extends SqlSessionDaoSupport {

	private String NAMESPACE;
	final static Class<? extends Object> SELF = BaseMybatisDao.class;
	protected final Log logger = LogFactory.getLog(BaseMybatisDao.class);
	/** 默认的查询Sql id */
	final static String DEFAULT_SQL_ID = "findAll";
	/** 默认的查询Count sql id **/
	final static String DEFAULT_COUNT_SQL_ID = "findCount";

	public BaseMybatisDao() {
		try {
			Object genericClz = getClass().getGenericSuperclass();
			if (genericClz instanceof ParameterizedType) {
				Class<T> entityClass = (Class<T>) ((ParameterizedType) genericClz).getActualTypeArguments()[0];
				NAMESPACE = entityClass.getPackage().getName() + "." + entityClass.getSimpleName();
			}
		} catch (RuntimeException e) {
			LoggerUtils.error(SELF, "初始化失败，继承BaseMybatisDao，没有泛型！");
		}
	}

	@Autowired
	public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
		super.setSqlSessionTemplate(sqlSessionTemplate);
	}

	/**
	 * 根据Sql id 去查询 分页对象
	 *
	 * @param sqlId
	 *            对应mapper.xml 里的Sql Id
	 * @param params
	 *            参数<String,Object>
	 * @param pageNo
	 *            number
	 * @param pageSize
	 *            size
	 * @return
	 */
	public Pagination<?> findByPageBySqlId(String sqlId, Map<String, Object> params, Integer pageNo, Integer pageSize) {

		pageNo = null == pageNo ? 1 : pageNo;
		pageSize = null == pageSize ? 10 : pageSize;

		sqlId = String.format("%s.%s", NAMESPACE, sqlId);

		Pagination<?> page = new Pagination<Object>();
		page.setPageNo(pageNo);
		page.setPageSize(pageSize);
		Configuration c = this.getSqlSession().getConfiguration();
		int offset = (page.getPageNo() - 1) * page.getPageSize();
		String page_sql = String.format(" limit %s , %s", offset, pageSize);
		params.put("page_sql", page_sql);

		BoundSql boundSql = c.getMappedStatement(sqlId).getBoundSql(params);
		String sqlcode = boundSql.getSql();

		LoggerUtils.debug(SELF, "findByPageBySqlId sql : %s", sqlcode);
		String countCode = "", countId = "";
		BoundSql countSql = null;
		/**
		 * sql id 和 count id 用同一个
		 */
		if (StringUtils.isBlank(sqlId)) {
			countCode = sqlcode;
			countSql = boundSql;
		} else {
			countId = sqlId;

			Map<String, Object> countMap = new HashMap<String, Object>();
			countMap.putAll(params);
			countMap.remove("page_sql");// 去掉，分页的参数。
			countSql = c.getMappedStatement(countId).getBoundSql(countMap);
			countCode = countSql.getSql();
		}
		try {
			Connection conn = this.getSqlSession().getConnection();

			List<?> resultList = this.getSqlSession().selectList(sqlId, params);
			page.setList(resultList);
			PreparedStatement ps = getPreparedStatement(countCode, countSql.getParameterMappings(), params, conn);
			ps.execute();
			ResultSet set = ps.getResultSet();

			while (set.next()) {
				page.setTotalCount(set.getInt(1));
			}
		} catch (Exception e) {
			LoggerUtils.error(SELF, "jdbc.error.code.findByPageBySqlId", e);
		}
		return page;
	}

	public List<?> findList(String sqlId, Object params, Integer pageNo, Integer pageSize) {
		Map<String, Object> map = BeanUtilsExt.pushMap(params);
		return findList(sqlId, map, pageNo, pageSize);
	}

	/**
	 * 根据Sql ID 查询List，而不要查询分页的页码
	 *
	 * @param sqlId
	 *            对应mapper.xml 里的Sql Id[主语句]
	 * @param params
	 *            参数<String,Object>
	 * @param pageNo
	 *            number
	 * @param pageSize
	 *            size
	 * @return
	 */
	public List<?> findList(String sqlId, Map<String, Object> params, Integer pageNo, Integer pageSize) {
		pageNo = null == pageNo ? 1 : pageNo;
		pageSize = null == pageSize ? 10 : pageSize;

		int offset = (pageNo - 1) * pageSize;
		String page_sql = String.format(" limit %s , %s", offset, pageSize);
		params.put("page_sql", page_sql);
		sqlId = String.format("%s.%s", NAMESPACE, sqlId);

        return this.getSqlSession().selectList(sqlId, params);
	}

	/**
	 * 当Sql ID 是 default findAll的情况下。
	 *
	 * @param params
	 * @param pageNo
	 * @param pageSize
	 *            返回的类型[可以不传参]
	 * @return
	 */
	public List<?> findList(Map<String, Object> params, Integer pageNo, Integer pageSize) {
		return findList(DEFAULT_SQL_ID, params, pageNo, pageSize);
	}

	public Pagination<?> findPage(String sqlId, String countId, Object params) {
		Map<String, Object> map = BeanUtilsExt.pushMap(params);
		Integer pageNo=(Integer) map.get("pageNo");
		Integer pageSize=(Integer) map.get("pageSize");
		return findPage(sqlId, countId, map, pageNo, pageSize);
	}


	public Pagination<?> findPage(String sqlId, String countId, Object params, Integer pageNo,
								  Integer pageSize) {
		Map<String, Object> map = BeanUtilsExt.pushMap(params);
		return findPage(sqlId, countId, map, pageNo, pageSize);
	}
	/**
	 * 分页
	 *
	 * @param sqlId
	 *            主语句
	 * @param countId
	 *            Count 语句
	 * @param params
	 *            参数
	 * @param pageNo
	 *            第几页
	 *            返回的类型[可以不传参]
	 * @return
	 */
	public Pagination<?> findPage(String sqlId, String countId, Map<String, Object> params, Integer pageNo,
			Integer pageSize) {
		pageNo = null == pageNo ? 1 : pageNo;
		pageSize = null == pageSize ? 10 : pageSize;
		Pagination<?> page = new Pagination<T>();
		page.setPageNo(pageNo);
		page.setPageSize(pageSize);
		int offset = (page.getPageNo() - 1) * page.getPageSize();
		String page_sql = String.format(" limit  %s , %s ", offset, pageSize);
		params.put("page_sql", page_sql);

		sqlId = String.format("%s.%s", NAMESPACE, sqlId);

		countId = String.format("%s.%s", NAMESPACE, countId);

		try {
			List<?> resultList = this.getSqlSession().selectList(sqlId, params);

			page.setList(resultList);

			page.setTotalCount(this.getSqlSession().selectOne(countId, params));

		} catch (Exception e) {
			LoggerUtils.error(SELF, "jdbc.error.code.findByPageBySqlId", e);
		}
		return page;

	}

	/**
	 * 分页并统计总金额
	 * @param sqlId   字段sql名称
	 * @param countId count sql名称
	 * @param coutnAoumt 统计金额sql名称
	 * @param params 参数
	 * @param pageNo 页码
	 * @param pageSize 页面显示数
	 * @return
	 */
	public Pagination<?> findPagePlus(String sqlId, String countId,String coutnAoumt, Map<String, Object> params, Integer pageNo,
								  Integer pageSize) {
		Pagination<?> page = findPage(sqlId, countId, params, pageNo, pageSize);
		PagintionPlus res= new PagintionPlus();
		try {
			BeanUtils.copyProperties(res,page);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		Map<String,Object> map= this.getSqlSession().selectOne(coutnAoumt, params);
		if (map==null){
			res.setTotalAmount(new BigDecimal(0));
		}else {
			Object totalAmount = map.get("totalAmount");
			if (totalAmount==null){
				res.setTotalAmount(new BigDecimal(0));
			}else {
				res.setTotalAmount(new BigDecimal(totalAmount.toString()));
			}
			Object totalNumber = map.get("totalNumber");
			if (totalNumber==null){
				res.setTotalNumber(null);
			}else {
				res.setTotalNumber(Integer.valueOf(totalNumber.toString()));
			}
		}
		return res;
	}

	/**
	 * 重载减少参数DEFAULT_SQL_ID, "findCount"
	 *
	 * @param params
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	public Pagination<?> findPage(Map<String, Object> params, Integer pageNo, Integer pageSize) {

		return findPage(DEFAULT_SQL_ID, DEFAULT_COUNT_SQL_ID, params, pageNo, pageSize);
	}

	/**
	 * 组装
	 *
	 * @param sql
	 * @param parameterMappingList
	 * @param params
	 * @param conn
	 * @return
	 * @throws SQLException
	 */
	private PreparedStatement getPreparedStatement(String sql, List<ParameterMapping> parameterMappingList,
			Map<String, Object> params, Connection conn) throws SQLException {
		/**
		 * 分页根据数据库分页
		 */
		MysqlDialect o = new MysqlDialect();

		PreparedStatement ps = conn.prepareStatement(o.getCountSqlString(sql));
		int index = 1;
		for (int i = 0; i < parameterMappingList.size(); i++) {
			ps.setObject(index++, params.get(parameterMappingList.get(i).getProperty()));
		}
		return ps;
	}




}
