package com.cm.common.plugin.dao.database.table.impl;

import com.cm.common.exception.SaveException;
import com.cm.common.exception.SearchException;
import com.cm.common.exception.UpdateException;
import com.cm.common.plugin.dao.database.table.ITableDao;
import com.cm.common.plugin.enums.database.table.ColumnDataTypeEnum;
import com.cm.common.plugin.enums.database.table.ColumnIsNullableEnum;
import com.cm.common.plugin.pojo.dtos.database.table.TableColumnDTO;
import com.cm.common.plugin.pojo.dtos.database.table.TableDTO;
import com.cm.common.plugin.pojo.vos.database.table.TableColumnVO;
import com.cm.common.plugin.pojo.vos.database.table.TableOldColumnVO;
import com.cm.common.utils.HashMapUtil;
import com.cm.common.utils.WStringUtil;
import com.cm.common.utils.jdbc.JdbcUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: TableDaoImpl
 * @Description: //TODO
 * @Author: WangGeng
 * @Date: 2019/11/15 15:39
 * @Version: 1.0
 **/
@Primary
@Repository
public class TableDaoImpl implements ITableDao {

    private static final Logger LOG = LoggerFactory.getLogger(TableDaoImpl.class);

    @Override
    public void saveTable(Connection connection, String databaseName, String tableName, List<TableColumnVO> tableColumns) {
        try {
            connection.setAutoCommit(false);
            String sql = getSaveTableSQL(databaseName, tableName, tableColumns);
            LOG.debug("sql: {}", sql);
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.execute();
            connection.commit();
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            try {
                connection.rollback();
            } catch (SQLException ex) {
                LOG.error(ex.getMessage(), ex);
            }
            throw new SaveException("建表失败");
        }
    }

    @Override
    public void removeTable(Connection connection, String databaseName, String tableName) {
        try {
            connection.setAutoCommit(true);
            String sql = String.format("DROP TABLE IF EXISTS `%s`.`%s`", databaseName, tableName);
            LOG.debug("sql: {}", sql);
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.execute();
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new SaveException("删表失败");
        }
    }

    @Override
    public void saveTableColumn(Connection connection, String databaseName, String tableName, TableColumnVO tableColumnVO) {
        try {
            connection.setAutoCommit(true);
            StringBuilder sqlSB = new StringBuilder(String.format("ALTER TABLE `%s`.`%s` ADD COLUMN ", databaseName, tableName));
            sqlSB.append(getSaveColumnSql(tableColumnVO));
            LOG.debug("sql: {}", sqlSB.toString());
            PreparedStatement preparedStatement = connection.prepareStatement(sqlSB.toString());
            preparedStatement.execute();
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new SaveException("新增列失败");
        }
    }

    @Override
    public void removeTableColumn(Connection connection, String databaseName, String tableName, String columnName) {
        try {
            connection.setAutoCommit(true);
            StringBuilder sqlSB = new StringBuilder(String.format("ALTER TABLE `%s`.`%s` DROP COLUMN %s", databaseName, tableName, WStringUtil.lowerUpper2UnderLine(columnName)));
            LOG.debug("sql: {}", sqlSB.toString());
            PreparedStatement preparedStatement = connection.prepareStatement(sqlSB.toString());
            preparedStatement.execute();
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new SaveException("删除列失败");
        }
    }

    @Override
    public void updateTableColumn(Connection connection, String databaseName, String tableName, TableOldColumnVO tableOldColumnVO) {
        try {
            connection.setAutoCommit(true);
            StringBuilder sqlSB = new StringBuilder(String.format("ALTER TABLE `%s`.`%s` CHANGE %s ", databaseName, tableName, WStringUtil.lowerUpper2UnderLine(tableOldColumnVO.getOldColumnName())));
            sqlSB.append(getSaveColumnSql(tableOldColumnVO));
            LOG.debug("sql: {}", sqlSB.toString());
            PreparedStatement preparedStatement = connection.prepareStatement(sqlSB.toString());
            preparedStatement.execute();
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new UpdateException("修改列属性失败");
        }
    }

    @Override
    public void updateTableColumnType(Connection connection, String databaseName, String tableName, TableColumnVO tableColumnVO) {
        try {
            connection.setAutoCommit(true);
            StringBuilder sqlSB = new StringBuilder(String.format("ALTER TABLE `%s`.`%s` MODIFY ", databaseName, tableName));
            sqlSB.append(getSaveColumnSql(tableColumnVO));
            LOG.debug("sql: {}", sqlSB.toString());
            PreparedStatement preparedStatement = connection.prepareStatement(sqlSB.toString());
            preparedStatement.execute();
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new UpdateException("修改列属性失败");
        }
    }

    @Override
    public List<TableColumnDTO> listTableColumns(Connection connection, Map<String, Object> params) {
        try {
            String sql = "SELECT COLUMN_NAME columnName,COLUMN_DEFAULT columnDefault,IS_NULLABLE isNullable,DATA_TYPE dataType,CHARACTER_MAXIMUM_LENGTH characterMaximumLength,NUMERIC_PRECISION numericPrecision,NUMERIC_SCALE numericScale,DATETIME_PRECISION datetimePrecision,COLUMN_TYPE columnType,COLUMN_KEY columnKey,COLUMN_COMMENT columnComment,EXTRA extra FROM `information_schema`.`COLUMNS` WHERE `TABLE_SCHEMA`= ? AND `TABLE_NAME`= ?";
            LOG.debug("sql: {}", sql);
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, params.get("databaseName").toString());
            preparedStatement.setString(2, params.get("tableName").toString());
            preparedStatement.execute();
            return HashMapUtil.mapListToBeanList(JdbcUtil.listResult(preparedStatement.getResultSet()), TableColumnDTO.class);
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new SearchException("获取表列失败");
        }
    }

    @Override
    public List<TableDTO> listTables(Connection connection, Map<String, Object> params) {
        try {
            String sql = "SELECT TABLE_SCHEMA tableSchema, TABLE_NAME tableName FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = ?";
            LOG.debug("sql: {}", sql);
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, params.get("databaseName").toString());
            preparedStatement.execute();
            return HashMapUtil.mapListToBeanList(JdbcUtil.listResult(preparedStatement.getResultSet()), TableDTO.class);
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new SearchException("获取表列表失败");
        }
    }

    @Override
    public TableDTO getTableByTableName(Connection connection, String databaseName, String tableName) {
        try {
            String sql = "SELECT TABLE_SCHEMA tableSchema, TABLE_NAME tableName FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?";
            LOG.debug("sql: {}", sql);
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, databaseName);
            preparedStatement.setString(2, tableName);
            preparedStatement.execute();
            Map<String, Object> tableMap = JdbcUtil.getResult(preparedStatement.getResultSet());
            if (tableMap == null) {
                return null;
            }
            return HashMapUtil.mapToBean(tableMap, TableDTO.class);
        } catch (SQLException e) {
            LOG.error(e.getMessage(), e);
            throw new SearchException("获取表失败");
        }
    }

    /**
     * 获取建表SQL语句
     *
     * @param databaseName
     * @param tableName
     * @param tableColumns
     * @return
     */
    private String getSaveTableSQL(String databaseName, String tableName, List<TableColumnVO> tableColumns) {
        String underLineTableName = WStringUtil.lowerUpper2UnderLine(tableName);
        StringBuilder sql = new StringBuilder("CREATE TABLE `").append(databaseName).append("`.`").append(tableName).append("`(");
        sql.append("`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',");
        sql.append(String.format("`%s_id` CHAR(36) NOT NULL COMMENT '主键',", underLineTableName));
        for (TableColumnVO tableColumn : tableColumns) {
            sql.append(getSaveColumnSql(tableColumn)).append(",");
        }
        sql.append("`creator` CHAR(36) DEFAULT NULL,");
        sql.append("`gmt_create` datetime DEFAULT NULL,");
        sql.append("`modifier` CHAR(36) DEFAULT NULL,");
        sql.append("`gmt_modified` datetime DEFAULT NULL,");
        sql.append("`is_delete` int(1) DEFAULT '0',");
        sql.append(String.format("PRIMARY KEY (`id`, `%s_id`)", underLineTableName));
        sql.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;");
        return sql.toString();
    }

    /**
     * 新增列的SQL
     *
     * @param tableColumn
     * @return
     */
    private String getSaveColumnSql(TableColumnVO tableColumn) {
        boolean notNull = true;
        StringBuilder columnSql = new StringBuilder();
        if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.VARCHAR.getDataType())) {
            columnSql.append(String.format("`%s` VARCHAR(%d)", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.INT.getDataType())) {
            columnSql.append(String.format("`%s` INT(%d)", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.DOUBLE.getDataType())) {
            columnSql.append(String.format("`%s` DOUBLE(%d, %d)", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength(), tableColumn.getColumnFloatLength()));
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.BIGINT.getDataType())) {
            columnSql.append(String.format("`%s` BIGINT(%d)", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.DATE.getDataType())) {
            columnSql.append(String.format("`%s` DATE", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.DATETIME.getDataType())) {
            columnSql.append(String.format("`%s` DATETIME", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.TEXT.getDataType())) {
            columnSql.append(String.format("`%s` TEXT DEFAULT NULL", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
            notNull = false;
        } else if (StringUtils.equals(tableColumn.getDataType(), ColumnDataTypeEnum.LONGTEXT.getDataType())) {
            columnSql.append(String.format("`%s` LONGTEXT DEFAULT NULL", WStringUtil.lowerUpper2UnderLine(tableColumn.getColumnName()), tableColumn.getColumnLength()));
            notNull = false;
        }
        if (notNull && StringUtils.equals(tableColumn.getIsNullable(), ColumnIsNullableEnum.YES.getIsNullable())) {
            columnSql.append(" NOT NULL");
        }
        if (!StringUtils.isEmpty(tableColumn.getColumnDefault())) {
            columnSql.append(String.format(" DEFAULT '%s'", tableColumn.getColumnDefault()));
        }
        if (!StringUtils.isEmpty(tableColumn.getColumnComment())) {
            columnSql.append(String.format(" COMMENT '%s'", tableColumn.getColumnComment()));
        }
        return columnSql.toString();
    }

}
