package com.template.common.utils;


import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DBUtil {
    private String url;
    private String user;
    private String password;
    private String driver;
    private String charset;
    private String str_link = "?useUnicode=true" +
            "&useSSL=false" +
            "&useAffectedRows=true" +
            "&allowPublicKeyRetrieval=true" +
            "&allowMultiQueries=true" +
            "&rewriteBatchedStatements=true" +
            "&serverTimezone=Asia/Shanghai";


    public DBUtil(String url, String user, String password, String driver, String charset) {
        this.url = url;
        this.user = user;
        this.password = password;
        this.driver = driver;
        if (charset != null && !charset.equals("")) {
            this.charset = "&characterEncoding=" + charset;
        }
    }

    public Map<String, Object> getConnection() {
        try {
            Class.forName(driver);
            Connection connection = DriverManager.getConnection(url + str_link + charset, user, password);
            return CommonUtil.getReturnMap("0", connection);
        } catch (SQLException | ClassNotFoundException e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        }
    }

    public Map<String, Object> query(Connection conn, String sql) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Map<String, Object> map = new HashMap<>();
        try {
            stmt = conn.prepareStatement(sql);
            rs = stmt.executeQuery();
        } catch (Exception e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        } finally {
            map.put("rs", rs);
            map.put("stmt", stmt);
        }

        return CommonUtil.getReturnMap("0", map);
    }

    public Map<String, Object> update(String sql) {
        Connection conn = null;
        PreparedStatement stmt = null;
        int count = 0;
        Map<String, Object> map = new HashMap<>();
        try {
            Map<String, Object> map_return = getConnection();
            if (map_return.get("code") == "0") {
                return CommonUtil.getReturnMap("1", map_return.get("msg"));
            }
            conn = (Connection) map_return.get("msg");
            stmt = conn.prepareStatement(sql);
            count = stmt.executeUpdate();
        } catch (Exception e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        } finally {
            map.put("count", count);
            map.put("stmt", stmt);
            map.put("conn", conn);
        }

        return CommonUtil.getReturnMap("0", map);
    }

    public Map<String, Object> update(Connection conn, String sql) {
        PreparedStatement stmt = null;
        int count = 0;
        Map<String, Object> map = new HashMap<>();
        try {
            stmt = conn.prepareStatement(sql);
            count = stmt.executeUpdate();
        } catch (Exception e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        } finally {
            map.put("count", count);
            map.put("stmt", stmt);
        }

        return CommonUtil.getReturnMap("0", map);
    }

    public Map<String, Object> getMetaDataByTable(Connection conn, String tableName) {
        // 获取数据表的列信息
        DatabaseMetaData dbMetaData;
        ResultSet rs = null;
        Map<Integer, Object> map = new HashMap<>();
        try {
            dbMetaData = conn.getMetaData();
            rs = dbMetaData.getColumns(null, null, tableName, null);
            List<Map<String, String>> list = new ArrayList<>();
            // 遍历列信息
            while (rs.next()) {
                Map<String, String> tmpMap = new HashMap<>();
                // 获取列名称
                String columnName = rs.getString("COLUMN_NAME");
                // 获取列类型
                String columnType = rs.getString("TYPE_NAME");
                // 获取列备注
                String columnComment = rs.getString("REMARKS");
                // 获取是否允许为空
                String isNullable = rs.getString("IS_NULLABLE");
                // 获取字段的长度
                int columnSize = rs.getInt("COLUMN_SIZE");

                tmpMap.put("COLUMN_NAME", columnName);
                tmpMap.put("COLUMN_TYPE", columnType);
                tmpMap.put("COLUMN_COMMENT", columnComment);
                tmpMap.put("COLUMN_SIZE", String.valueOf(columnSize));
                tmpMap.put("IS_NULLABLE", isNullable);

                list.add(tmpMap);
            }

            return CommonUtil.getReturnMap("0", list);
        } catch (SQLException e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        } finally {
            this.closeResultSet(rs);
            this.closeConnection(conn);
        }
    }

    public Map<String, Object> getTableMetaData(Connection conn) {
        try {
            // 获取数据库元数据
            DatabaseMetaData metaData = conn.getMetaData();

            // 获取所有表名
            ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"});
            List<String> list = new ArrayList<>();
            while (tables.next()) {
                String tableName = tables.getString("TABLE_NAME");
                String comment = getTableComment(metaData, tableName);
                System.out.println(tableName + "[" + comment + "]");
                list.add(tableName + "[" + comment + "]");
            }

            // 关闭资源
            tables.close();
            conn.close();
            return CommonUtil.getReturnMap("0", list);
        } catch (SQLException e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        }
    }

    /**
     * 获取表的备注
     *
     * @param metaData  数据库元数据
     * @param tableName 表名
     * @return 表的备注
     * @throws SQLException
     */
    private static String getTableComment(DatabaseMetaData metaData, String tableName) throws SQLException {
        String comment = null;
        ResultSet resultSet = metaData.getTables(null, null, tableName, null);
        if (resultSet.next()) {
            comment = resultSet.getString("REMARKS");
        }
        resultSet.close();
        return comment;
    }

    public Map<String, Object> getMetaDataBySql(Connection conn, String sql) {
        // 执行SELECT语句
        Statement stmt;
        ResultSet rs;
        ResultSetMetaData rsmd;
        int columnCount;
        try {
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            rsmd = rs.getMetaData();
            // 获取字段数量
            columnCount = rsmd.getColumnCount();
        } catch (SQLException e) {
            return CommonUtil.getReturnMap("1", e.getMessage());
        }

        List<Map<String, String>> list = new ArrayList<>();
        // 遍历字段信息
        for (int i = 1; i <= columnCount; i++) {
            Map<String, String> tmpMap = new HashMap<>();
            try {
                String columnName = rsmd.getColumnName(i);
                String columnType = rsmd.getColumnTypeName(i);
                String columnDisplaySize = String.valueOf(rsmd.getColumnDisplaySize(i));
                String columnComment = rsmd.getColumnLabel(i);

                tmpMap.put("COLUMN_NAME", columnName);
                tmpMap.put("COLUMN_TYPE", columnType);
                tmpMap.put("COLUMN_COMMENT", columnComment);
                tmpMap.put("COLUMN_SIZE", columnDisplaySize);

                list.add(tmpMap);
            } catch (SQLException e) {
                return CommonUtil.getReturnMap("1", e.getMessage());
            } finally {
                this.closeResultSet(rs);
                this.closeStatement(stmt);
                this.closeConnection(conn);
            }
        }

        return CommonUtil.getReturnMap("0", list);
    }

    public void closeResultSet(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }
    }

    public void closeStatement(Statement stmt) {
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }
    }

    public void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }
    }
}
