package com.qqchen.deploy.backend.framework.generator;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器工具类
 */
@Slf4j
public class CodeGeneratorUtils {

    @Data
    public static class FieldInfo {
        private String fieldName;        // 字段名

        private String fieldType;        // 字段类型

        private String columnName;       // 数据库列名

        private String comment;          // 注释

        private boolean nullable;        // 是否可空

        private boolean isPrimaryKey;    // 是否主键
    }

    @Data
    public static class GeneratorConfig {
        private String moduleName;           // 模块名称（中文）

        private String className;            // 类名

        private String basePackage;          // 基础包路径

        private String outputPath;           // 输出路径

        private String tableName;            // 表名

        private String urlPath;              // URL路径

        private List<FieldInfo> fields;      // 字段信息
        
        private boolean isThirdPartyApi;     // 是否为三方API（true: 三方API, false: 二方API）
    }

    private static final Configuration configuration;

    static {
        configuration = new Configuration(Configuration.VERSION_2_3_32);
        configuration.setClassLoaderForTemplateLoading(CodeGeneratorUtils.class.getClassLoader(), "templates");
        configuration.setDefaultEncoding("UTF-8");
    }

    /**
     * 生成所有代码文件
     *
     * @param createTableSql CREATE TABLE SQL语句
     * @param config         生成器配置
     */
    public static void generateCode(String createTableSql, GeneratorConfig config) {
        try {
            // 解析SQL获取字段信息
            config.setFields(parseSqlFields(createTableSql));

            // 生成各种类型的文件
            generateFile("entity.ftl", config, "/entity/" + config.getClassName() + ".java");
            generateFile("dto.ftl", config, "/dto/" + config.getClassName() + "DTO.java");
            generateFile("query.ftl", config, "/query/" + config.getClassName() + "Query.java");
            generateFile("repository.ftl", config, "/repository/I" + config.getClassName() + "Repository.java");
            generateFile("service.ftl", config, "/service/I" + config.getClassName() + "Service.java");
            generateFile("serviceImpl.ftl", config, "/service/impl/" + config.getClassName() + "ServiceImpl.java");
            generateFile("converter.ftl", config, "/converter/" + config.getClassName() + "Converter.java");

            // 根据API类型生成不同的Controller
            if (config.isThirdPartyApi()) {
                // 三方API，生成在api包下，类名为XxxApiController
                generateFile("controller.ftl", config, "/api/" + config.getClassName() + "ApiController.java");
            } else {
                // 二方API，生成在controller包下，类名为XxxController
                generateFile("controller.ftl", config, "/controller/" + config.getClassName() + "Controller.java");
            }

            log.info("代码生成完成，输出路径: {}", config.getOutputPath());
        } catch (Exception e) {
            log.error("代码生成失败", e);
            throw new RuntimeException("代码生成失败", e);
        }
    }

    /**
     * 解析CREATE TABLE SQL获取字段信息
     */
    private static List<FieldInfo> parseSqlFields(String createTableSql) {
        List<FieldInfo> fields = new ArrayList<>();
        // 移除多余的空白字符和换行
        createTableSql = createTableSql.replaceAll("\\s+", " ").trim();
        
        // 提取表定义部分
        int startIndex = createTableSql.indexOf("(");
        int endIndex = createTableSql.lastIndexOf(")");
        if (startIndex == -1 || endIndex == -1) {
            throw new IllegalArgumentException("无效的CREATE TABLE语句");
        }
        
        // 获取字段定义部分
        String fieldDefinitions = createTableSql.substring(startIndex + 1, endIndex);
        
        // 按逗号分割字段定义
        String[] fieldDefs = fieldDefinitions.split(",");
        
        for (String fieldDef : fieldDefs) {
            fieldDef = fieldDef.trim();
            
            // 跳过主键定义等非字段定义行
            if (fieldDef.toLowerCase().startsWith("primary key") || 
                fieldDef.toLowerCase().startsWith("key") ||
                fieldDef.toLowerCase().startsWith("constraint") ||
                fieldDef.toLowerCase().startsWith("index") ||
                fieldDef.isEmpty()) {
                continue;
            }
            
            // 解析字段定义
            String[] parts = fieldDef.split("\\s+", 3);
            if (parts.length < 2) {
                continue;
            }
            
            FieldInfo field = new FieldInfo();
            
            // 设置字段名（移除反引号）
            field.setColumnName(parts[0].replace("`", ""));
            
            // 设置字段类型
            String typeStr = parts[1].toUpperCase();
            if (typeStr.contains("(")) {
                typeStr = typeStr.substring(0, typeStr.indexOf("("));
            }
            field.setFieldType(sqlTypeToJavaType(typeStr));
            
            // 设置字段名（驼峰命名）
            field.setFieldName(columnToField(field.getColumnName()));
            
            // 设置是否可空
            field.setNullable(!fieldDef.toLowerCase().contains("not null"));
            
            // 设置注释
            int commentIndex = fieldDef.toLowerCase().indexOf("comment");
            if (commentIndex != -1) {
                String comment = fieldDef.substring(commentIndex + 7).trim();
                // 提取引号中的注释内容
                if (comment.startsWith("'") && comment.endsWith("'")) {
                    field.setComment(comment.substring(1, comment.length() - 1));
                }
            }
            
            // 设置是否为主键
            field.setPrimaryKey(fieldDef.toLowerCase().contains("primary key"));
            
            // 基础字段不需要生成
            if (!isBaseField(field.getColumnName())) {
                fields.add(field);
            }
        }
        
        return fields;
    }
    
    /**
     * 判断是否为基础字段（这些字段由父类Entity提供）
     */
    private static boolean isBaseField(String columnName) {
        return columnName.equals("id") ||
               columnName.equals("create_by") ||
               columnName.equals("create_time") ||
               columnName.equals("update_by") ||
               columnName.equals("update_time") ||
               columnName.equals("version") ||
               columnName.equals("deleted");
    }

    /**
     * 生成单个文件
     */
    private static void generateFile(String templateName, GeneratorConfig config, String subPath) throws IOException, TemplateException {
        Template template = configuration.getTemplate(templateName);
        String outputPath = config.getOutputPath() + subPath;
        File outputFile = new File(outputPath);

        // 确保目录存在
        outputFile.getParentFile().mkdirs();

        // 准备数据模型
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("basePackage", config.getBasePackage());
        dataModel.put("moduleName", config.getModuleName());
        dataModel.put("className", config.getClassName());
        dataModel.put("tableName", config.getTableName());
        dataModel.put("fields", config.getFields());
        dataModel.put("urlPath", config.getUrlPath());
        dataModel.put("isThirdPartyApi", config.isThirdPartyApi());

        // 生成文件
        try (Writer writer = new FileWriter(outputFile)) {
            template.process(dataModel, writer);
        }

        log.info("生成文件: {}", outputPath);
    }

    /**
     * 数据库列名转Java字段名
     */
    private static String columnToField(String columnName) {
        String[] parts = columnName.toLowerCase().split("_");
        StringBuilder result = new StringBuilder(parts[0]);
        for (int i = 1; i < parts.length; i++) {
            result.append(StringUtils.capitalize(parts[i]));
        }
        return result.toString();
    }

    /**
     * SQL类型转Java类型
     */
    private static String sqlTypeToJavaType(String sqlType) {
        sqlType = sqlType.toUpperCase();
        switch (sqlType) {
            case "VARCHAR":
            case "CHAR":
            case "TEXT":
                return "String";
            case "INT":
            case "INTEGER":
                return "Integer";
            case "BIGINT":
                return "Long";
            case "DECIMAL":
                return "BigDecimal";
            case "DATETIME":
            case "TIMESTAMP":
                return "LocalDateTime";
            case "DATE":
                return "LocalDate";
            case "BIT":
            case "TINYINT":
                return "Boolean";
            default:
                return "String";
        }
    }
}