package com.starry.generator.core;

import com.starry.generator.config.GeneratorConfig;
import com.starry.generator.entity.GenTableColumnEntity;
import com.starry.generator.entity.GenTableEntity;
import com.starry.generator.utils.GenUtils;
import com.starry.generator.utils.VelocityInitializer;
import com.starry.generator.utils.VelocityUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

/**
 * 核心代码生成器
 *
 * @author admin
 * @since 2024-01-01
 */
@Slf4j
public class CodeGenerator {

    private final GeneratorConfig config;

    public CodeGenerator(GeneratorConfig config) {
        this.config = config;
    }

    /**
     * 生成代码
     *
     * @param tableNames
     *            表名数组
     */
    public void generateCode(String[] tableNames) throws Exception {
        // 初始化 Velocity
        VelocityInitializer.initVelocity();

        // 创建输出目录
        createOutputDirectory();

        for (String tableName : tableNames) {
            log.info("开始生成表 {} 的代码", tableName);
            generateTableCode(tableName);
            log.info("表 {} 代码生成完成", tableName);
        }
    }

    /**
     * 生成单个表的代码
     *
     * @param tableName
     *            表名
     */
    private void generateTableCode(String tableName) throws Exception {
        // 获取表信息
        GenTableEntity table = getTableInfo(tableName);
        if (table == null) {
            log.warn("表 {} 不存在，跳过生成", tableName);
            return;
        }

        // 获取表字段信息
        List<GenTableColumnEntity> columns = getTableColumns(tableName);
        table.setColumns(columns);

        // 初始化表信息
        initTableInfo(table);

        // 初始化字段信息
        initColumnInfo(table);

        // 设置主键列信息
        setPkColumn(table);

        // 设置模板变量信息
        VelocityContext context = VelocityUtils.prepareContext(table);

        // 获取模版列表
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());

        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);

            // 写入文件
            writeToFile(template, table, sw.toString());
        }
    }

    /**
     * 获取表信息
     */
    private GenTableEntity getTableInfo(String tableName) throws Exception {
        String sql = "SELECT table_name, table_comment FROM information_schema.tables "
                + "WHERE table_schema = ? AND table_name = ?";

        try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {

            String dbName = getDatabaseName();
            log.info("查询表信息 - 数据库名: {}, 表名: {}", dbName, tableName);

            stmt.setString(1, dbName);
            stmt.setString(2, tableName);

            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    GenTableEntity table = new GenTableEntity();
                    table.setTableName(rs.getString("table_name"));
                    table.setTableComment(rs.getString("table_comment"));
                    log.info("找到表: {}, 注释: {}", table.getTableName(), table.getTableComment());
                    return table;
                } else {
                    // 如果找不到表，尝试查询所有表来调试
                    log.warn("未找到表 {}，正在查询数据库 {} 中的所有表...", tableName, dbName);
                    showAllTables(conn, dbName);
                }
            }
        }
        return null;
    }

    /**
     * 显示数据库中的所有表（用于调试）
     */
    private void showAllTables(Connection conn, String dbName) throws Exception {
        String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = ? LIMIT 10";
        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, dbName);
            try (ResultSet rs = stmt.executeQuery()) {
                log.info("数据库 {} 中的表:", dbName);
                boolean hasTable = false;
                while (rs.next()) {
                    hasTable = true;
                    log.info("  - {}", rs.getString("table_name"));
                }
                if (!hasTable) {
                    log.warn("数据库 {} 中没有找到任何表，请检查数据库名称是否正确", dbName);
                }
            }
        }
    }

    /**
     * 获取表字段信息
     */
    private List<GenTableColumnEntity> getTableColumns(String tableName) throws Exception {
        List<GenTableColumnEntity> columns = new ArrayList<>();

        String sql = "SELECT column_name, data_type, column_comment, is_nullable, "
                + "column_key, column_default, extra FROM information_schema.columns "
                + "WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position";

        try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {

            String dbName = getDatabaseName();
            stmt.setString(1, dbName);
            stmt.setString(2, tableName);

            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    GenTableColumnEntity column = new GenTableColumnEntity();
                    column.setColumnName(rs.getString("column_name"));
                    column.setColumnType(rs.getString("data_type"));
                    column.setColumnComment(rs.getString("column_comment"));
                    column.setIsRequired("NO".equals(rs.getString("is_nullable")) ? "1" : "0");
                    column.setIsPk("PRI".equals(rs.getString("column_key")) ? "1" : "0");
                    column.setIsIncrement("auto_increment".equals(rs.getString("extra")) ? "1" : "0");

                    columns.add(column);
                }
            }
        }

        return columns;
    }

    /**
     * 初始化表信息
     */
    private void initTableInfo(GenTableEntity table) {
        table.setClassName(GenUtils.convertClassName(table.getTableName()));
        table.setPackageName(config.getPackageName());
        table.setModuleName(GenUtils.getModuleName(config.getPackageName()));
        table.setBusinessName(GenUtils.getBusinessName(table.getTableName()));
        table.setFunctionName(table.getTableComment());
        table.setFunctionAuthor(config.getAuthor());
        table.setTplCategory(config.getTplCategory());
    }

    /**
     * 初始化字段信息
     */
    private void initColumnInfo(GenTableEntity table) {
        if (table.getColumns() != null) {
            for (GenTableColumnEntity column : table.getColumns()) {
                GenUtils.initColumnField(column, table);
            }
        }
    }

    /**
     * 设置主键列信息
     */
    private void setPkColumn(GenTableEntity table) {
        for (GenTableColumnEntity column : table.getColumns()) {
            if ("1".equals(column.getIsPk())) {
                table.setPkColumn(column);
                break;
            }
        }

        if (table.getPkColumn() == null) {
            table.setPkColumn(table.getColumns().get(0));
        }
    }

    /**
     * 写入文件
     */
    private void writeToFile(String template, GenTableEntity table, String content) throws IOException {
        String fileName = getFileName(template, table);
        File file = new File(fileName);

        // 创建父目录
        file.getParentFile().mkdirs();

        try (FileWriter writer = new FileWriter(file)) {
            writer.write(content);
        }

        log.info("生成文件：{}", fileName);
    }

    /**
     * 获取文件名
     */
    private String getFileName(String template, GenTableEntity table) {
        String outputDir = config.getOutputDir();
        String packageName = table.getPackageName();
        String moduleName = table.getModuleName();
        String businessName = table.getBusinessName();
        String className = table.getClassName();

        String javaPath = outputDir + "/src/main/java/" + packageName.replace(".", "/");
        String resourcesPath = outputDir + "/src/main/resources";

        if (template.contains("entity.java.vm")) {
            return javaPath + "/entity/" + className + "Entity.java";
        } else if (template.contains("mapper.java.vm")) {
            return javaPath + "/mapper/" + className + "Mapper.java";
        } else if (template.contains("service.java.vm")) {
            return javaPath + "/service/I" + className + "Service.java";
        } else if (template.contains("serviceImpl.java.vm")) {
            return javaPath + "/service/impl/" + className + "ServiceImpl.java";
        } else if (template.contains("controller.java.vm")) {
            return javaPath + "/controller/" + className + "Controller.java";
        } else if (template.contains("queryVo.java.vm")) {
            return javaPath + "/vo/" + className + "QueryVo.java";
        } else if (template.contains("addVo.java.vm")) {
            return javaPath + "/vo/" + className + "AddVo.java";
        } else if (template.contains("mapper.xml.vm")) {
            return resourcesPath + "/mapper/" + className + "Mapper.xml";
        } else if (template.contains("sql.vm")) {
            return outputDir + "/" + businessName + "Menu.sql";
        } else if (template.contains("api.js.vm")) {
            return outputDir + "/api/" + moduleName + "/" + businessName + ".js";
        } else if (template.contains("index.vue.vm")) {
            return outputDir + "/views/" + moduleName + "/" + businessName + "/index.vue";
        }

        return outputDir + "/" + template.replace(".vm", "");
    }

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

    /**
     * 从URL中获取数据库名
     */
    private String getDatabaseName() {
        return config.getDatabaseName();
    }

    /**
     * 创建输出目录
     */
    private void createOutputDirectory() {
        File outputDir = new File(config.getOutputDir());
        if (!outputDir.exists()) {
            outputDir.mkdirs();
        }
    }
}
