package easydo.technology.components;

import com.baomidou.mybatisplus.annotation.TableId;
import easydo.technology.annotation.TableKey;
import easydo.technology.core.ZeroSpringFolder;
import easydo.technology.utils.MapUtil;
import easydo.technology.utils.ResultSetUtil;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

//version_1.0 whz 20230725
//version_2.0 whz 20230802修复分页where条件ambiguous的问题
//version_3.0 whz 20230830修复字段里有关键字的问题
//version_4.0 whz 20240119修复修改和删除会更新整表的操作
//version_4.1 whz 20240130修复修改不能改字段为空的问题
//version_4.2 whz 20240227修复in中不能使用非String类型的字段问题
@Component
public class JdbcClient {
    @Resource
    DataSource dataSource;

    /**
     * 处理连接
     *
     * @param connection
     * @param statements
     */
    public void finallyExecute(Connection connection, Statement... statements) {
        if (connection != null) {
            try {
                connection.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (statements.length > 0) {
            for (Statement statement : statements) {
                try {
                    statement.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void finallyExecute(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void rollback(Connection connection) {
        try {
            if (connection != null) {
                connection.rollback();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 单表分页查询,含实体类
     *
     * @param map
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> Map<String, Object> getJdbcPage(Map<String, Object> map, Class<T> clazz, Connection... connections) throws Exception {
        String tableName = ResultSetUtil.humpToLine(clazz.getSimpleName());
        Map<String, Object> result = new HashMap<>();
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            String condition = ResultSetUtil.formatSqlCondition(map);
            sql = "select * from " + tableName + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<T> resultList = ResultSetUtil.formatResultSetToList(resultSet, clazz);

            map.remove("current");
            map.remove("size");
            map.remove("orderBy");
            condition = ResultSetUtil.formatSqlCondition(map);
            sql = "select count(1) from " + tableName + condition;
            resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                result.put("total", resultSet.getInt(1));
            }
            result.put("records", resultList);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 连表分页查询
     *
     * @param map
     * @param listSql
     * @return
     * @throws Exception
     */
    public Map<String, Object> getJdbcPageViewMap(Map<String, Object> map, String listSql, Connection... connections) throws Exception {
        Map<String, Object> result = new HashMap<>();
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            String condition = ResultSetUtil.formatSqlCondition(map);
            String viewSql = "select v.* from (" + listSql + ") v";
            sql = viewSql + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<Map<String, Object>> resultList = ResultSetUtil.formatResultSetToListMap(resultSet);

            map.remove("current");
            map.remove("size");
            map.remove("orderBy");
            condition = ResultSetUtil.formatSqlCondition(map);
            String countSql = "select count(1) from (" + listSql + ") v";
            sql = countSql + condition;
            resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                result.put("total", resultSet.getInt(1));
            }
            result.put("records", resultList);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 连表分页查询,含实体类
     *
     * @param map
     * @param listSql
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> Map<String, Object> getJdbcPageView(Map<String, Object> map, String listSql, Class<T> clazz, Connection... connections) throws Exception {
        Map<String, Object> result = new HashMap<>();
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            String condition = ResultSetUtil.formatSqlCondition(map);
            String viewSql = "select v.* from (" + listSql + ") v";
            sql = viewSql + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<T> resultList = ResultSetUtil.formatResultSetToList(resultSet, clazz);

            map.remove("current");
            map.remove("size");
            map.remove("orderBy");
            condition = ResultSetUtil.formatSqlCondition(map);
            String countSql = "select count(1) from (" + listSql + ") v";
            sql = countSql + condition;
            resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                result.put("total", resultSet.getInt(1));
            }
            result.put("records", resultList);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 单表，多表查询集合
     *
     * @param map
     * @param listSql
     * @return
     * @throws Exception
     */
    public List<Map<String, Object>> getJdbcListMap(Map<String, Object> map, String listSql, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            String condition = ResultSetUtil.formatSqlCondition(map);
            String viewSql = "select v.* from (" + listSql + ") v";
            sql = viewSql + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<Map<String, Object>> resultList = ResultSetUtil.formatResultSetToListMap(resultSet);
            return resultList;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 单表查询集合，含实体类
     *
     * @param map
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> List<T> getJdbcList(Map<String, Object> map, Class<T> clazz, Connection... connections) throws Exception {
        String tableName = ResultSetUtil.humpToLine(clazz.getSimpleName());
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            String condition = ResultSetUtil.formatSqlCondition(map);
            sql = "select * from " + tableName + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<T> resultList = ResultSetUtil.formatResultSetToList(resultSet, clazz);
            return resultList;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public <T> List<T> getJdbcList(T object, Connection... connections) throws Exception {
        String tableName = ResultSetUtil.humpToLine(object.getClass().getSimpleName());
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            Map<String, Object> map = MapUtil.objectToMap(object);
            String condition = ResultSetUtil.formatSqlCondition(map);
            sql = "select * from " + tableName + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<T> resultList = (List<T>) ResultSetUtil.formatResultSetToList(resultSet, object.getClass());
            return resultList;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 多表查询集合，含实体类
     *
     * @param map
     * @param listSql
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> List<T> getJdbcList(Map<String, Object> map, String listSql, Class<T> clazz, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            String condition = ResultSetUtil.formatSqlCondition(map);
            String viewSql = "select v.* from (" + listSql + ") v";
            sql = viewSql + condition;
            ResultSet resultSet = statement.executeQuery(sql);
            List<T> resultList = ResultSetUtil.formatResultSetToList(resultSet, clazz);
            return resultList;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }


    /**
     * 单表查询字段，含实体类
     *
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public <T> T getJdbcModel(T obj, Connection... connections) throws Exception {
        String condition = ResultSetUtil.formatSelectSql(obj);
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = condition;
            ResultSet resultSet = statement.executeQuery(sql);

            return (T) ResultSetUtil.formatResultSetToModel(resultSet, obj.getClass());
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public <T> T getJdbcModelByMap(Map<String, Object> map, Class<T> clazz, Connection... connections) throws Exception {
        String condition = ResultSetUtil.formatSelectSql(map, clazz);
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();
            sql = condition;
            ResultSet resultSet = statement.executeQuery(sql);

            return (T) ResultSetUtil.formatResultSetToModel(resultSet, clazz);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public <T> T getJdbcModelByMap(Map<String, Object> map, String sql, Class<T> clazz, Connection... connections) throws Exception {
        String condition = ResultSetUtil.formatSelectSql(map, sql);
        Connection connection = null;
        Statement statement = null;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();
            sql = condition;
            ResultSet resultSet = statement.executeQuery(sql);

            return (T) ResultSetUtil.formatResultSetToModel(resultSet, clazz);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 单表根据id查询字段，含实体类
     *
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public <T> T getJdbcModelById(T obj, Connection... connections) throws Exception {
        String condition = ResultSetUtil.formatSelectByIdSql(obj);

        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = condition;
            ResultSet resultSet = statement.executeQuery(sql);

            return (T) ResultSetUtil.formatResultSetToModel(resultSet, obj.getClass());
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 新增
     *
     * @param model
     * @throws Exception
     */
    public void jdbcInsert(Object model, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatInsertSql(model);
            statement.executeUpdate(sql);

            if (model instanceof ZeroSpringFolder) {
                ZeroSpringFolder zsf = (ZeroSpringFolder) model;
                ZeroSpringFolderComponent.build(dataSource).save(zsf);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public void jdbcInsertExcludeFile(Object model, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatInsertSql(model);
            statement.executeUpdate(sql);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 修改
     *
     * @param model
     * @param whereMap
     * @throws Exception
     */
    public void jdbcUpdate(Object model, Map<String, Object> whereMap, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatUpdateSql(model, whereMap);
            statement.executeUpdate(sql);

            if (model instanceof ZeroSpringFolder) {
                ZeroSpringFolder zsf = (ZeroSpringFolder) model;
                ZeroSpringFolderComponent.build(dataSource).save(zsf);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 根据id修改
     *
     * @param model
     * @throws Exception
     */
    public void jdbcUpdateById(Object model, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            Field[] declaredFields = model.getClass().getDeclaredFields();
            Map<String, Object> map = new HashMap<>();
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(TableKey.class)) {
                    field.setAccessible(true);
                    map.put(field.getName(), field.get(model));
                    break;
                }
                if (field.isAnnotationPresent(TableId.class)) {
                    field.setAccessible(true);
                    map.put(field.getAnnotation(TableId.class).value(), field.get(model));
                    break;
                }
            }
            if (map.isEmpty()) {
                Field idField = model.getClass().getDeclaredField("id");
                idField.setAccessible(true);
                map.put("id", idField.get(model));
            }

            sql = ResultSetUtil.formatUpdateSql(model, map);
            statement.executeUpdate(sql);

            if (model instanceof ZeroSpringFolder) {
                ZeroSpringFolder zsf = (ZeroSpringFolder) model;
                ZeroSpringFolderComponent.build(dataSource).save(zsf);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 根据id删除
     *
     * @param model
     * @throws Exception
     */


    public void jdbcRemoveById(Object model, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatRemoveByIdSql(model);
            statement.executeUpdate(sql);

            if (model instanceof ZeroSpringFolder) {
                ZeroSpringFolder zsf = (ZeroSpringFolder) model;
                ZeroSpringFolderComponent.build(dataSource).clean(zsf);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public void jdbcRemoveByIdExcludeFile(Object model, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatRemoveByIdSql(model);
            statement.executeUpdate(sql);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public void jdbcRemove(Object model, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatRemoveSql(model);
            statement.executeUpdate(sql);

            if (model instanceof ZeroSpringFolder) {
                ZeroSpringFolder zsf = (ZeroSpringFolder) model;
                ZeroSpringFolderComponent.build(dataSource).clean(zsf);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    /**
     * 统计条数
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public int getJdbcCount(Object obj, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatSelectCountSql(obj);
            ResultSet resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                return resultSet.getInt(1);
            }
            return 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }

    public <T> int getJdbcCountByMap(Map<String, Object> map, Class<T> clazz, Connection... connections) throws Exception {
        Connection connection = null;
        Statement statement = null;
        String sql;
        try {
            if (connections.length > 0) {
                connection = connections[0];
            } else {
                connection = dataSource.getConnection();
            }
            statement = connection.createStatement();

            sql = ResultSetUtil.formatSelectCountSql(clazz, map);
            ResultSet resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                return resultSet.getInt(1);
            }
            return 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (connections.length == 0) {
                finallyExecute(connection, statement);
            } else {
                finallyExecute(statement);
            }
        }
    }
}
