package com.ywt.core;

import com.ywt.core.utils.Checker;
import com.ywt.core.utils.StringHelper;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.persistence.Table;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by dyh2020
 */
public class SqlHelper {

    JdbcTemplate jdbcTemplate;

    /**
     * 下载起始页：默认第一页
     */
    public static final int DOWNLOAD_PAGE_INDEX = 1;

    /**
     * 下载页数大小：默认1000
     */
    public static final int DOWNLOAD_PAGE_SIZE = 1000;


    public SqlHelper(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    /**
     * 按条件分页
     *
     * @param pageIndex   当前页数
     * @param pageSize    每页大小
     * @param fields      需要返回的列
     * @param where       where条件
     * @param orderBy     分组
     * @param entityClass 映射实体
     * @param params      参数组
     * @param <T>         范型
     * @return 分页的结果集
     */
    public <T> PagedList<T> getPagedList(int pageIndex, int pageSize, String fields, String where, String orderBy, Class<T> entityClass, Object[] params) {
        if (pageIndex < 1) {
            pageIndex = 1;
        }
        if (pageSize < 1) {
            pageSize = 10;
        }
        Table table = entityClass.getAnnotation(Table.class);
        String tableName = table != null ? table.name() : entityClass.getSimpleName();
        tableName = String.format("`%s`", tableName);

        int total = getSearchTotal(tableName, where, params);
        String sql = getSearchSql(fields, tableName, where, orderBy);
        // System.out.println(sql);
        List<T> list = queryListByParams(pageIndex, pageSize, sql, params, entityClass);
        return new PagedList<>(pageIndex, pageSize, total, list);
    }

    /**
     * 按条件获取list数据，用于下载
     *
     * @param fields      需要返回的列
     * @param where       where条件
     * @param orderBy     分组
     * @param entityClass 映射实体
     * @param params      参数组
     * @param <T>         范型
     * @return 分页的结果集
     */
    public <T> List<T> getDownLoadList(String fields, String where, String orderBy, Class<T> entityClass, Object[] params) {
        Table table = entityClass.getAnnotation(Table.class);
        String tableName = table != null ? table.name() : entityClass.getSimpleName();
        tableName = String.format("`%s`", tableName);

        int total = getSearchTotal(tableName, where, params);
        String sql = getSearchSql(fields, tableName, where, orderBy);
        return getDownLoadListAsync(total, sql, params, entityClass);
    }

    /**
     * 按条件分页查找（分组）
     *
     * @param pageIndex   当前页数
     * @param pageSize    每页大小
     * @param fields      需要返回的列
     * @param where       where条件
     * @param orderBy     分组
     * @param entityClass 映射实体
     * @param params      参数组
     * @param <T>         范型
     * @return 分页的结果集（分组）
     */
    public <T> PagedList<T> getGroupByPagedList(int pageIndex, int pageSize, String fields, String where, String orderBy, Class<T> entityClass, Object[] params) {
        if (pageIndex < 1) {
            pageIndex = 1;
        }
        if (pageSize < 1) {
            pageSize = 10;
        }

        Table table = entityClass.getAnnotation(Table.class);
        String tableName = table != null ? table.name() : entityClass.getSimpleName();
        tableName = String.format("`%s`", tableName);

        int total = getGroupBySearchTotal(tableName, where, params);
        String sql = getSearchSql(fields, tableName, where, orderBy);
        List<T> list = queryListByParams(pageIndex, pageSize, sql, params, entityClass);
        return new PagedList<>(pageIndex, pageSize, total, list);
    }

    /**
     * 适用于分组的分页查找（不分页）
     *
     * @param fields      需要返回的列
     * @param where       where条件
     * @param orderBy     按什么进行分组
     * @param entityClass 实体
     * @param params      参数组
     * @param <T>         范型
     * @return list结果集
     */
    public <T> List<T> getList(String fields, String where, String orderBy, Class<T> entityClass, Object[] params) {
        Table table = entityClass.getAnnotation(Table.class);
        String tableName = table != null ? table.name() : entityClass.getSimpleName();
        tableName = String.format("`%s`", tableName);

        String sql = getSearchSql(fields, tableName, where, orderBy);
        return jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(entityClass));
    }

    private String buildWhereSql(String where) {
        if (StringHelper.isNullOrEmpty(where)) {
            return " ";
        }

        return String.format(" where %s ", where);
    }

    /**
     * 对时间的封装(仅用于查询)
     *
     * @param whereSql        sql
     * @param paramList       参数集合
     * @param searchTime      需要查询的字段
     * @param searchStartTime 查询时间开始时间
     * @param searchEndTime   查询时间结束时间
     * @throws ParseException 解析异常
     */
    public Map<String, Object> timeQuerySql(String whereSql, List<Object> paramList, String searchTime, String searchStartTime, String searchEndTime) throws ParseException {
        String formatDate = "yyyy-MM-dd";
        SimpleDateFormat format = new SimpleDateFormat(formatDate);
        HashMap<String, Object> map = new HashMap<>(16);

        //下单时间查询
        if (!Checker.isNull(searchStartTime)) {
            whereSql += " and ( " + searchTime + " >= ?)";
            Date date = format.parse(searchStartTime);
            paramList.add(date);
        }

        if (!Checker.isNull(searchEndTime)) {
            whereSql += " and ( " + searchTime + " < ? )";
            Date date = format.parse(searchEndTime);

            //把日期往后增加一天.整数往后推,负数往前移动
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(date);
            //这个时间就是日期往后推一天的结果
            calendar.add(Calendar.DATE, 1);
            date = calendar.getTime();
            paramList.add(date);
        }
        map.put("whereSql", whereSql);
        map.put("paramList", paramList);
        return map;
    }

    /**
     * 对ids的查找(仅用于查询)
     *
     * @param whereSql    sql
     * @param paramList   参数集合
     * @param searchValue 需要查询的字段
     * @param valueIds    数值Ids
     */
    public Map<String, Object> idsQuerySql(String whereSql, List<Object> paramList, String searchValue, String valueIds) {
        StringBuilder stringBuilder = new StringBuilder();
        HashMap<String, Object> map = new HashMap<>(16);
        if (!Checker.isNull(valueIds)) {
            String[] valueIdArr = valueIds.split(",");
            int size = valueIdArr.length;
            stringBuilder.append(" and ( ");
            for (int i = 0; i < size; i++) {
                if (i == 0) {
                    stringBuilder.append(" ").append(searchValue).append(" = ? ");
                } else {
                    stringBuilder.append(" or ").append(searchValue).append(" = ? ");
                }
                paramList.add(Integer.parseInt(valueIdArr[i]));
            }
            stringBuilder.append(" ) ");
        }
        whereSql += stringBuilder.toString();
        map.put("whereSql", whereSql);
        map.put("paramList", paramList);
        return map;
    }

    /**
     * 得到多张表的数据分页(也可用于单表，但是需要传入对应的格式)
     *
     * @param pageIndex   当前页数
     * @param pageSize    每页大小
     * @param fields      行数
     * @param dataSource  资料来源，比如（feedback f join user u on f.user_id = u.id）
     * @param where       需要查找的行数
     * @param orderBy     按参数排序
     * @param params      参数
     * @param <T>         映射对象
     * @param entityClass 接收参数返回的对象
     * @return <T>
     */
    public <T> PagedList<T> getMultipleTablePagedList(int pageIndex, int pageSize, String fields, String dataSource, String where, String orderBy, Object[] params, Class<T> entityClass) {
        if (pageIndex < 1) {
            pageIndex = 1;
        }
        if (pageSize < 1) {
            pageSize = 10;
        }
        // 总记录数
        int total = getSearchTotal(dataSource, where, params);
        // sql语句
        String sql = getSearchSql(fields, dataSource, where, orderBy);
        // 查找单页数据
        List<T> list = queryListByParams(pageIndex, pageSize, sql, params, entityClass);
        return new PagedList<>(pageIndex, pageSize, total, list);
    }

    /**
     * 得到查询的总条数
     *
     * @param dataSource 数据源：表名，一张或多张表
     * @param where      sql的where条件
     * @param params     对应的参数组
     * @return 总条数
     */
    public int getSearchTotal(String dataSource, String where, Object[] params) {
        String whereSql = buildWhereSql(where);
        String countSql = String.format("select count(1) from %s %s;", dataSource, whereSql);
        // 总记录数
        return Checker.getIntegerValue(jdbcTemplate.queryForObject(countSql, params, Integer.class));

    }

    /**
     * 得到查询的总条数
     *
     * @param dataSource 数据源：表名，一张或多张表
     * @param where      sql的where条件
     * @param params     对应的参数组
     * @return 总条数
     */
    private int getGroupBySearchTotal(String dataSource, String where, Object[] params) {
        String whereSql = buildWhereSql(where);
        String countSql = String.format("select count(1) from %s %s;", dataSource, whereSql);
        // 总记录数， 这里需要用一个对象去接收，用 Integer 会报错
        List<Integer> totalList = jdbcTemplate.queryForList(countSql, params, Integer.class);
        return Checker.isNone(totalList) ? 0 : totalList.size();
    }

    /**
     * 拼接得到查询的sql
     *
     * @param fields     需要查找的字段
     * @param dataSource 数据源：表名，一张或多张表
     * @param where      sql的where条件
     * @param orderBy    分组的条件
     * @return sql语句
     */
    private String getSearchSql(String fields, String dataSource, String where, String orderBy) {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("select ");
        if (StringHelper.isNullOrEmpty(fields)) {
            sqlBuilder.append("*");
        } else {
            sqlBuilder.append(fields);
        }
        sqlBuilder.append(" from ").append(dataSource);

        String whereSql = buildWhereSql(where);
        sqlBuilder.append(whereSql);

        if (!StringHelper.isNullOrEmpty(orderBy)) {
            sqlBuilder.append(" order by ").append(orderBy);
        }
        return sqlBuilder.toString();
    }


    /**
     * 按条件查询，并返回list结果
     *
     * @param pageIndex   当前页数
     * @param pageSize    每页大小
     * @param sql         sql语句
     * @param params      参数
     * @param entityClass 类
     * @param <T>         范型
     * @return list结果集
     */
    private <T> List<T> queryListByParams(int pageIndex, int pageSize, String sql, Object[] params, Class<T> entityClass) {
        sql += String.format(" limit %d, %d;", (pageIndex - 1) * pageSize, pageSize);
        return jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(entityClass));
    }

    /**
     * 多张表分页下载（返回list）
     * 里面用到多线程进行分页获取数据，防止一次性从数据库读取数据过多而导致数据库的压力很大
     *
     * @param fields      需要查找的字段
     * @param dataSource  数据源：表名，一张或多张表
     * @param where       sql的where条件
     * @param orderBy     分组的条件
     * @param params      对应的参数组
     * @param entityClass 实体
     * @param <T>         范型
     * @return 返回list
     */
    public <T> List<T> getMultipleTableDownLoadList(String fields, String dataSource, String where, String orderBy, Object[] params, Class<T> entityClass) {
        int total = getSearchTotal(dataSource, where, params);
        String sql = getSearchSql(fields, dataSource, where, orderBy);
        return getDownLoadListAsync(total, sql, params, entityClass);
    }

    /**
     * 多线程查找数据，用于下载
     * 参考文章：https://www.cnblogs.com/dennyzhangdd/p/7010972.html#_label3
     *
     * @param total       总条数
     * @param sql         sql语句
     * @param params      参数
     * @param entityClass 类
     * @param <T>         范型
     * @return list结果集
     */
    private <T> List<T> getDownLoadListAsync(int total, String sql, Object[] params, Class<T> entityClass) {
        // 得到总页数，并进行多线程查找获取list中的值
        int pageIndex = DOWNLOAD_PAGE_INDEX;
        int pageSize = DOWNLOAD_PAGE_SIZE;
        int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;

        List<T> resultList = new ArrayList<>();
        //定长8线程池
        ExecutorService exs = Executors.newFixedThreadPool(8);
        try {
            List<Integer> pageIndexList = new ArrayList<>();
            for (int i = 1; i <= totalPage; i++) {
                pageIndexList.add(i);
            }
            @SuppressWarnings("rawtypes")
            CompletableFuture[] cfs = pageIndexList.stream().map(currentPage -> CompletableFuture.supplyAsync(() -> queryListByParams(currentPage, pageSize, sql, params, entityClass), exs)
                            //如需获取任务完成先手顺序，此处代码即可
                            .whenComplete((v, e) -> {
                                System.out.println(String.format("分页查找完毕：页数：%d  每页大小：%d", currentPage, pageSize));
                                resultList.addAll(v);
                            }))
                    .toArray(CompletableFuture[]::new);
            //封装后无返回值，必须自己whenComplete()获取
            CompletableFuture.allOf(cfs).join();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            exs.shutdown();
        }
        return resultList;
    }

    public int getTotalPages(int total, int pageSize) {
        return total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
    }

    public <T> List<T> getPagedList3(String fields, String from, String where,
                                     String orderBy, Class<T> entityClass, Object[] params) {

        String whereSql = buildWhereSql(where);
        String countSql = String.format("select count(1) from %s %s;", from, whereSql);

        //总记录数
        //  int total = jdbcTemplate.queryForObject(countSql, params, Integer.class);

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append("select ");

        if (StringHelper.isNullOrEmpty(fields)) {
            sqlBuilder.append("*");
        } else {
            sqlBuilder.append(fields);
        }

        sqlBuilder.append(" from " + from);
        sqlBuilder.append(whereSql);

        if (!StringHelper.isNullOrEmpty(orderBy)) {
            sqlBuilder.append(" order by " + orderBy);
        }

        List<T> list = jdbcTemplate.query(sqlBuilder.toString(), params, new BeanPropertyRowMapper<>(entityClass));
        if (!Checker.isNone(list)){
            return list;
        } else {
            return new ArrayList<>();
        }


    }
}
