package cd.casic.plugin.utils.dBUtils;

import cd.casic.plugin.core.register.database.DBEnums;
import cd.casic.plugin.core.register.database.DataBaseProperty;
import cd.casic.plugin.utils.dBUtils.annotation.Entity;
import cd.casic.plugin.utils.dBUtils.annotation.SQL;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.mysql.cj.jdbc.MysqlDataSource;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.stereotype.Component;
import org.sqlite.SQLiteDataSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * @description: TODO
 * @author: mianbin
 * @date: 2025/5/22 17:09
 * @version: 1.0
 */
@Data
@Slf4j
@Component
public class SQLGenerator {

    @Resource
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    private String mapToSqlType(Class<?> fieldType) {
        // 简化的类型映射，针对MySQL数据库
        if (fieldType == int.class || fieldType == Integer.class) {
            return "INTEGER";
        } else if (fieldType == long.class || fieldType == Long.class) {
            return "BIGINT";
        } else if (fieldType == float.class || fieldType == Float.class) {
            return "FLOAT";
        } else if (fieldType == double.class || fieldType == Double.class) {
            return "DOUBLE";
        } else if (fieldType == boolean.class || fieldType == Boolean.class) {
            return "BOOLEAN"; // 或者 "TINYINT(1)"
        } else if (fieldType == String.class) {
            return "VARCHAR(255)";
        } else if (fieldType == java.util.Date.class || fieldType == java.sql.Date.class) {
            return "DATETIME"; // 或者 "TIMESTAMP"
        } else if (fieldType == java.sql.Timestamp.class) {
            return "TIMESTAMP";
        } else if (fieldType == byte[].class) {
            return "BLOB";
        } else if (fieldType.isEnum()) {
            return "ENUM"; // 需要进一步处理，如列出所有枚举值
        } else if (fieldType.isArray() || Collection.class.isAssignableFrom(fieldType)) {
            throw new IllegalArgumentException("Arrays and Collections are not directly supported. Consider using a join table or serialization.");
        } else {
            throw new IllegalArgumentException("Unsupported field type: " + fieldType.getName());
        }
    }

    public void sqlDeleteGenerator(Class<?> c, DataBaseProperty dataBaseProperty, String pluginId) {
        StringBuilder sql = new StringBuilder("\n");
        String tableName = "plugin_" + (c.toString().substring(c.toString().lastIndexOf(".") + 1)).toLowerCase();

        sql.append("drop table if exists ").append(tableName);
        String dbPath = "ops-module-plugins/plugins/" + pluginId + "/datasource/database.db";

        this.executeSql(sql.toString(), dataBaseProperty, pluginId);
        log.info(sql.toString());
    }

    public void sqlGenerator(Class<?> c, DataBaseProperty dataBaseProperty, String pluginId) {
        String dbPath = "ops-module-plugins/plugins/" + pluginId + "/datasource/database.db";
        Entity entityAnnotation = c.getAnnotation(Entity.class);
        if (ObjectUtils.isEmpty(entityAnnotation)) {
            return;
        }
        StringBuilder sql = new StringBuilder("\n");
        String tableName = "plugin_" + (c.toString().substring(c.toString().lastIndexOf(".") + 1)).toLowerCase();

        sql.append("create table ").append(tableName).append("(").append("\n");
        int count = c.getDeclaredFields().length;
        int index = 0;
        boolean pkFlag = false;
        for (Field field : c.getDeclaredFields()) {
            SQL annotation = field.getAnnotation(SQL.class);
            index += 1;
            sql.append(field.getName()).append(" ").append(mapToSqlType(field.getType())).append(" ");
            if (ObjectUtils.isNotEmpty(annotation) && ObjectUtils.isNotEmpty(annotation.defaultValue())) {
                sql.append(" default " + "'").append(annotation.defaultValue()).append("'");
            }
            if (ObjectUtils.isNotEmpty(annotation) && annotation.notNull()) {
                sql.append(" not null ");
            }
            if (ObjectUtils.isNotEmpty(annotation) && annotation.pK()) {
                // TODO 这是如果dataBaseProperty是null（即使用主程序的数据源时），数据库类型先写死。
                DBEnums type = dataBaseProperty == null ? DBEnums.MYSQL : dataBaseProperty.type();
                if (pkFlag) {
                    log.error("Duplicate Primary Key");
                    throw new RuntimeException("Duplicate Primary Key");
                }
                this.genPrimaryKey(sql, type, tableName);
                pkFlag = true;
                if (annotation.autoIncrement()) {
                    this.genAutoIncrement(sql, type);
                }
            }
            if (index != count) {
                sql.append("," + "\n");
            } else {
                sql.append("\n");
            }
        }
        sql.append(")");
        log.info(sql.toString());
        this.executeSql(sql.toString(), dataBaseProperty, pluginId);
    }

    public void destroyDataSource(DataBaseProperty dataBaseProperty, String pluginId) {
        if (dataBaseProperty == null) return;
        String dataSourceId = pluginId + "-" + dataBaseProperty.type().getDBType();
        if (dynamicRoutingDataSource.getDataSources().containsKey(dataSourceId)) {
            dynamicRoutingDataSource.removeDataSource(dataSourceId);
        }
    }

    private void executeSql(String sql, DataBaseProperty dataBaseProperty, String pluginId) {
        try {
            Connection connection;
            if (Objects.isNull(dataBaseProperty)) {
                connection = dynamicRoutingDataSource.getConnection();
            } else {
                String dataSourceId = pluginId + "-" + dataBaseProperty.type().getDBType();
                if (!dynamicRoutingDataSource.getDataSources().containsKey(dataSourceId)) {
                    DataSource dataSource;
                    if (dataBaseProperty.type().equals(DBEnums.SQLITE)) {
                        dataSource = DataSourceBuilder.create().type(SQLiteDataSource.class)
                                .driverClassName(dataBaseProperty.driver())
                                .url(dataBaseProperty.url()).build();
                    } else {
                        dataSource = DataSourceBuilder.create()
                                .type(MysqlDataSource.class)
                                .driverClassName(dataBaseProperty.driver())
                                .username(dataBaseProperty.username()).password(dataBaseProperty.password())
                                .url(dataBaseProperty.url()).build();
                    }
                    dynamicRoutingDataSource.addDataSource(dataSourceId, dataSource);
                }
                connection = dynamicRoutingDataSource.getDataSource(dataSourceId).getConnection();
            }
            connection.createStatement().execute(sql);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    private void genPrimaryKey(StringBuilder sql, DBEnums dbEnums, String tableName) {
        switch (dbEnums) {
            case MYSQL:
                sql.append("primary key ");
                break;
            case SQLITE:
                sql.append("constraint ").append(tableName).append("_pk primary key ");
                break;
            default:
                log.error("Unsupported Database");
                throw new RuntimeException("Unsupported Database");
        }
    }

    private void genAutoIncrement(StringBuilder sql, DBEnums dbEnums) {
        switch (dbEnums) {
            case MYSQL:
                sql.append(" AUTO_INCREMENT");
                break;
            case SQLITE:
                sql.append(" AUTOINCREMENT");
                break;
            default:
                log.error("Unsupported Database");
                throw new RuntimeException("Unsupported Database");
        }
    }

    private String getSQLiteDBLocation(String pluginRoot, ClassLoader classLoader, String pluginId) {
        List<String> pluginFolders = new ArrayList<>();
        try {
            Path rootPath = Paths.get(pluginRoot);
            if (!Files.isDirectory(rootPath)) {
                System.out.println("提供的路径不是一个有效的目录：" + pluginRoot);
            }
            this.findPluginFoldersRecursively(rootPath, pluginFolders);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return "s";
    }

    private void findPluginFoldersRecursively(Path folder, List<String> pluginFolders) throws IOException {
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(folder)) {
            for (Path entry : stream) {
                if (Files.isDirectory(entry)) {
                    String folderName = entry.getFileName().toString();
                    if (folderName.contains("datasource")) {
                        pluginFolders.add(entry.toString());
                    }
                    findPluginFoldersRecursively(entry, pluginFolders); // 递归查找子目录
                }
            }
        }
    }

}
