package com.ag.log;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.ag.log.pojo.LogForCdc;
import com.ag.log.service.CdcDataDict;
import com.ag.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.*;

/**
 * Created by yangxh on 2022/5/26.
 */
public class LogForCdcUtil {

    private static Log logger = LogFactory.getLog(LogForCdcUtil.class);

    @Test
    public void testAdd() throws Exception {
        String addJsonStr = "{\"tablekey\":\"55\",\"database\":\"mysqlcdc\",\"data\":{\"name\":\"55555\",\"id\":\"55\",\"add1\":\"11\",\"add2\":\"22\"}," +
                "\"type\":\"insert\",\"table\":\"new_table\"}";
        execute(addJsonStr);
        logger.info("insert日志数据保存成功！");
    }
    @Test
    public void testEdit() throws Exception {
        String editJsonStr = "{\"tablekey\":\"333\",\"database\":\"mysqlcdc\",\"data\":{\"name\":\"ggggbbb\",\"id\":\"333\"}," +
                "\"beforedata\":{\"name\":\"gggg\",\"id\":\"333\"},\"type\":\"update\",\"table\":\"new_table\"}";
        execute(editJsonStr);
        logger.info("update日志数据保存成功！");
    }
    @Test
    public void testDel() throws Exception {
        String delJsonStr = "{\"tablekey\":\"333\",\"database\":\"mysqlcdc\",\"beforedata\":{\"name\":\"ggggbbb\",\"id\":\"333\"}," +
                "\"type\":\"delete\",\"table\":\"new_table\"}";
        execute(delJsonStr);
        logger.info("delete日志数据保存成功！");
    }

    @Test
    public void dropTable() throws Exception{
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml", "classpath:dynamicModule/modulelink.xml");
        JdbcTemplate jdbc = (JdbcTemplate) context.getBean("jdbcTemplateCdc");
        jdbc.update("DROP TABLE new_table");
        //jdbc.update("alter table new_table add column addCol varchar(20)");
        logger.info("测试日志表已删除！");
    }

    /**
     * 1.判断缓存是否存在
     * 2.判断表是否存在
     * 3.建表加入缓存
     * 4.判断字段是否追加
     * 5.增加字段并加入缓存
     * @param jsonStr
     * @return
     * @throws Exception
     */
    public int execute(String jsonStr) throws Exception {
        LogForCdc log = JSON.parseObject(jsonStr, LogForCdc.class);
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml", "classpath:dynamicModule/modulelink.xml");
        JdbcTemplate jdbc = (JdbcTemplate) context.getBean("jdbcTemplateCdc");
        String sql = null;
        if (!isExistsTable(jdbc.getDataSource().getConnection(), log)) {
            createTable(jdbc, log);
        }else {
            Set<String> colNames = getColumnsByTab(jdbc.getDataSource().getConnection(), log);
            Set<String> dataSet, addColSet = new HashSet<>();
            if (StrUtil.equals("delete", log.getType())) {
                dataSet = log.getBeforedata().keySet();
            } else {
                dataSet = log.getData().keySet();
            }
            for (String param : dataSet){
                if (!colNames.contains(param)){
                    addColSet.add(param);
                }
            }
            if (CollectionUtil.isNotEmpty(addColSet)) {
                addColumns(jdbc, log, addColSet);
            }
        }

        log.putData();
        if (StrUtil.equals("insert", log.getType()) || StrUtil.equals("update", log.getType())) {
            sql = mapToSql(log, log.getData());
        }
        if (StrUtil.equals("delete", log.getType())) {
            sql = mapToSql(log, log.getBeforedata());
        }
        return jdbc.update(sql);
    }

    public String mapToSql(LogForCdc log, JSONObject data) {
        String col = "", val = "";
        Assert.notEmpty(data.entrySet(), "非法的新增数据！");
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            col += entry.getKey() + ",";
            if (Convert.toStr(entry.getValue()).startsWith("to_date('")) {
                val += entry.getValue() + ",";
            } else {
                val += "'" + entry.getValue() + "',";
            }
        }
        String sql = "insert into " + log.getTable();
        sql += StrUtil.join("(", subLastStr(col), ")");
        sql += StrUtil.join(" values (", subLastStr(val), ")");
        return sql;
    }

    private String subLastStr(String param) {
        return param.substring(0, param.length() - 1);
    }

    public static boolean isExistsTable(Connection conn, LogForCdc log) throws Exception {
        DatabaseMetaData dbMetaData = conn.getMetaData();
        String[] types = {"TABLE"};
        ResultSet tabs = null;
        try {
            tabs = dbMetaData.getTables(null, null, log.getTable(), types);
            if (tabs.next()) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            tabs.close();
            conn.close();
        }
        return false;
    }

    public static int createTable(JdbcTemplate jdbc, LogForCdc log) {
        String buildSql = StrUtil.join("CREATE TABLE `", log.getTable(), "` (");
        buildSql += StrUtil.join(" `type` varchar(8),");
        buildSql += StrUtil.join(" `created_on` datetime,");
        buildSql += StrUtil.join(" `created_by` varchar(30),");
        //buildSql += StrUtil.join(" `id` int(11) NOT NULL AUTO_INCREMENT,");
        Set<String> set = log.getData().keySet();
        for (String key : set) {
            buildSql += StrUtil.join("`" + key + "` varchar(255),");
        }
        buildSql += StrUtil.join(" `tablekey` varchar(255)");
        buildSql += StrUtil.join(") ENGINE=InnoDB DEFAULT CHARSET=utf8");
        try {
            jdbc.update(buildSql);
            logger.info("建表成功："+log.getTable());
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static Set<String> getColumnsByTab(Connection conn, LogForCdc log) throws Exception{
        DatabaseMetaData dbMetaData = conn.getMetaData();
        ResultSet rs = dbMetaData.getColumns(null, null, log.getTable(), "%");
        Set<String> columns = new HashSet<>();
        while (rs.next()){
            columns.add(rs.getString(4));
        }
        return columns;
    }

    public static void addColumns(JdbcTemplate jdbc, LogForCdc log, Collection<String> addCols) {
        try {
            for (String addCol : addCols) {
                String buildSql = StrUtil.join("alter table ", log.getTable(), " add column ", addCol, " varchar(255)");
                jdbc.update(buildSql);
                logger.info(log.getTable()+"表增加字段："+addCol);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
