package com.emoon.extent.migration.service.impl;

import com.emoon.extent.migration.dto.DatabaseConfig;
import com.emoon.extent.migration.dto.MigrationRequest;
import com.emoon.extent.migration.dto.MigrationResult;
import com.emoon.extent.migration.service.DatabaseMigrationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.sql.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 数据库迁移服务实现
 *
 * @author destiny
 * @date 2025/11/21
 */
@Slf4j
@Service
public class DatabaseMigrationServiceImpl implements DatabaseMigrationService {

    @Override
    public MigrationResult migrateTables(MigrationRequest request) {
        long startTime = System.currentTimeMillis();
        MigrationResult result = new MigrationResult().setSuccess(false);

        try {
            // 验证数据库连接
            if (!validateConnection(request.getSrc())) {
                return result.setSuccess(false).setMessage("源数据库连接失败");
            }
            if (!validateConnection(request.getTgt())) {
                return result.setSuccess(false).setMessage("目标数据库连接失败");
            }

            // 获取所有需要迁移的表
            List<String> tablesToMigrate = getTablesToMigrate(request);
            if (tablesToMigrate.isEmpty()) {
                return result.setSuccess(true).setMessage("没有找到需要迁移的表");
            }

            result.setMigratedTables(new ArrayList<>());
            result.setTableInfos(new ArrayList<>());

            Connection srcConn = null;
            Connection tgtConn = null;

            try {
                srcConn = getConnection(request.getSrc());
                tgtConn = getConnection(request.getTgt());

                // 禁用自动提交
                tgtConn.setAutoCommit(false);

                AtomicLong totalRecords = new AtomicLong(0);

                // 逐个迁移表
                for (String tableName : tablesToMigrate) {
                    MigrationResult.TableMigrationInfo tableInfo = migrateTable(
                        srcConn, tgtConn, tableName, request.getBatchSize());

                    result.getTableInfos().add(tableInfo);
                    if (tableInfo.getSuccess()) {
                        result.getMigratedTables().add(tableName);
                        totalRecords.addAndGet(tableInfo.getRecordCount());
                        log.info("表 {} 迁移成功，迁移记录数: {}", tableName, tableInfo.getRecordCount());
                    } else {
                        log.error("表 {} 迁移失败: {}", tableName, tableInfo.getError());
                    }
                }

                // 提交事务
                tgtConn.commit();

                result.setSuccess(true);
                result.setTotalRecords(totalRecords.get());
                result.setDuration(System.currentTimeMillis() - startTime);
                result.setMessage(String.format("成功迁移 %d 个表，共 %d 条记录",
                    result.getMigratedTables().size(), totalRecords.get()));

            } catch (Exception e) {
                if (tgtConn != null) {
                    try {
                        tgtConn.rollback();
                    } catch (SQLException rollbackEx) {
                        log.error("回滚事务失败", rollbackEx);
                    }
                }
                log.error("数据库迁移失败", e);
                result.setSuccess(false).setMessage("数据库迁移失败: " + e.getMessage());
            } finally {
                closeConnection(srcConn);
                closeConnection(tgtConn);
            }

        } catch (Exception e) {
            log.error("迁移过程发生异常", e);
            result.setSuccess(false).setMessage("迁移过程发生异常: " + e.getMessage());
        }

        return result;
    }

    @Override
    public boolean validateConnection(DatabaseConfig databaseConfig) {
        Connection connection = null;
        try {
            connection = getConnection(databaseConfig);
            return connection != null && !connection.isClosed();
        } catch (Exception e) {
            log.error("数据库连接验证失败", e);
            return false;
        } finally {
            closeConnection(connection);
        }
    }

    /**
     * 获取需要迁移的表列表
     */
    private List<String> getTablesToMigrate(MigrationRequest request) throws SQLException {
        List<String> tables = new ArrayList<>();

        if (request.getTables() != null && !request.getTables().isEmpty()) {
            // 使用指定的表列表
            tables.addAll(request.getTables());
        } else {
            // 获取源数据库的所有表
            Connection srcConn = null;
            try {
                srcConn = getConnection(request.getSrc());
                DatabaseMetaData metaData = srcConn.getMetaData();
                ResultSet rs = metaData.getTables(null, null, "%", new String[]{"TABLE"});

                while (rs.next()) {
                    tables.add(rs.getString("TABLE_NAME"));
                }
            } finally {
                closeConnection(srcConn);
            }
        }

        return tables;
    }

    /**
     * 迁移单个表
     */
    private MigrationResult.TableMigrationInfo migrateTable(Connection srcConn, Connection tgtConn,
                                                           String tableName, int batchSize) throws SQLException {
        long startTime = System.currentTimeMillis();
        MigrationResult.TableMigrationInfo tableInfo = new MigrationResult.TableMigrationInfo()
            .setTableName(tableName).setSuccess(false);

        try {
            // 获取表结构并创建表
            String createTableSql = getCreateTableSql(srcConn, tableName);
            try (Statement stmt = tgtConn.createStatement()) {
                stmt.execute("DROP TABLE IF EXISTS " + tableName);
                stmt.execute(createTableSql);
            }

            // 迁移数据
            String selectSql = "SELECT * FROM " + tableName;
            String insertSql = generateInsertSql(srcConn, tableName);

            try (PreparedStatement selectStmt = srcConn.prepareStatement(selectSql);
                 PreparedStatement insertStmt = tgtConn.prepareStatement(insertSql);
                 ResultSet rs = selectStmt.executeQuery()) {

                ResultSetMetaData metaData = rs.getMetaData();
                int columnCount = metaData.getColumnCount();

                long recordCount = 0;

                while (rs.next()) {
                    for (int i = 1; i <= columnCount; i++) {
                        Object value = rs.getObject(i);
                        insertStmt.setObject(i, value);
                    }
                    insertStmt.addBatch();

                    recordCount++;

                    // 批量执行
                    if (recordCount % batchSize == 0) {
                        insertStmt.executeBatch();
                        tgtConn.commit();
                    }
                }

                // 执行剩余的批次
                insertStmt.executeBatch();

                tableInfo.setRecordCount(recordCount);
                tableInfo.setSuccess(true);
                tableInfo.setDuration(System.currentTimeMillis() - startTime);
            }

        } catch (Exception e) {
            log.error("迁移表 {} 失败", tableName, e);
            tableInfo.setSuccess(false).setError(e.getMessage());
        }

        return tableInfo;
    }

    /**
     * 获取建表SQL
     */
    private String getCreateTableSql(Connection connection, String tableName) throws SQLException {
        DatabaseMetaData metaData = connection.getMetaData();
        try (ResultSet rs = metaData.getColumns(null, null, tableName, null)) {
            StringBuilder sql = new StringBuilder("CREATE TABLE " + tableName + " (");

            List<String> columns = new ArrayList<>();
            List<String> primaryKeys = new ArrayList<>();

            while (rs.next()) {
                String columnName = rs.getString("COLUMN_NAME");
                String columnType = rs.getString("TYPE_NAME");
                int columnSize = rs.getInt("COLUMN_SIZE");
                boolean nullable = rs.getString("IS_NULLABLE").equals("YES");

                String columnDef = columnName + " " + columnType;
                if (columnSize > 0 && (columnType.contains("CHAR") || columnType.contains("BINARY"))) {
                    columnDef += "(" + columnSize + ")";
                }

                if (!nullable) {
                    columnDef += " NOT NULL";
                }

                columns.add(columnDef);
            }

            // 获取主键信息
            try (ResultSet pkRs = metaData.getPrimaryKeys(null, null, tableName)) {
                while (pkRs.next()) {
                    primaryKeys.add(pkRs.getString("COLUMN_NAME"));
                }
            }

            sql.append(String.join(", ", columns));

            if (!primaryKeys.isEmpty()) {
                sql.append(", PRIMARY KEY (").append(String.join(", ", primaryKeys)).append(")");
            }

            sql.append(")");

            return sql.toString();
        }
    }

    /**
     * 生成插入SQL
     */
    private String generateInsertSql(Connection connection, String tableName) throws SQLException {
        DatabaseMetaData metaData = connection.getMetaData();
        try (ResultSet rs = metaData.getColumns(null, null, tableName, null)) {
            List<String> columns = new ArrayList<>();

            while (rs.next()) {
                columns.add(rs.getString("COLUMN_NAME"));
            }

            String columnList = String.join(", ", columns);
            String valuePlaceholders = String.join(", ", Collections.nCopies(columns.size(), "?"));

            return "INSERT INTO " + tableName + " (" + columnList + ") VALUES (" + valuePlaceholders + ")";
        }
    }

    /**
     * 获取数据库连接
     */
    private Connection getConnection(DatabaseConfig config) throws SQLException {
        return DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
    }

    /**
     * 关闭数据库连接
     */
    private void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                if (!connection.isClosed()) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("关闭数据库连接失败", e);
            }
        }
    }
}