package com.jy.generator.service;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.fill.Property;
import com.jy.framework.model.base.BaseIdPo;
import com.jy.generator.builder.GeneratorBuilder;
import com.jy.generator.config.BeetlTemplateEngine;
import com.jy.generator.domain.GenCodeVo;
import com.jy.generator.domain.GeneratorConfig;
import org.apache.ibatis.type.JdbcType;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;

@Service
public class IndexService {

    private static final BeetlTemplateEngine templateEngine = new BeetlTemplateEngine();
    private static final GeneratorConfig config = GeneratorBuilder.config;

    public void genCode(GenCodeVo genCodeVo) {

        // 是否覆盖
        boolean override = genCodeVo.isOverride();

        // 生成文件
        List<Integer> outputFiles = genCodeVo.getOutputFiles();

        // 表名
        String tableName = genCodeVo.getTableName();

        // 表前缀
        String tablePrefix = tableName.split("_")[0];

        // 表后缀
        String tableSuffix = StrUtil.toCamelCase(tableName.substring(tableName.indexOf("_") + 1));

        // 模块名称
        String moduleName = config.isPrefixSubcontracting() ? tablePrefix : StringPool.EMPTY;

        // 代码生成
        FastAutoGenerator.create(config.getUrl(), config.getUsername(), config.getPassword())
                .templateEngine(templateEngine)
                .dataSourceConfig(builder ->
                        builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {

                            JdbcType jdbcType = metaInfo.getJdbcType();
                            if (JdbcType.TINYINT.equals(jdbcType)) {
                                return DbColumnType.INTEGER;
                            }

                            return typeRegistry.getColumnType(metaInfo);
                        }))
                .globalConfig(builder -> builder
                        // 设置作者
                        .author(genCodeVo.getAuthor())
                        // 指定输出目录
                        .outputDir(config.getDir() + config.getModule() + "//src//main//java")
                        // 时间类型对应策略
                        .dateType(DateType.ONLY_DATE)
                        // 禁止打开输出目录
                        .disableOpenDir()
                )
                .packageConfig(builder -> builder
                        // 设置父包名
                        .parent(config.getParent())
                        .moduleName(moduleName)
                        .entity("model.entity")
                )
                .strategyConfig(builder -> {

                    builder.addInclude(tableName)

                            // entity
                            .entityBuilder()
                            .enableLombok()
                            .disableSerialVersionUID()
                            .superClass(BaseIdPo.class)
                            .versionPropertyName("mpVersion")
                            .addTableFills(new Property("mpVersion", FieldFill.INSERT))
                            .logicDeletePropertyName("delFlag")
                            .addTableFills(new Property("delFlag", FieldFill.INSERT))
                            .addTableFills(new Property("createUser", FieldFill.INSERT))
                            .addTableFills(new Property("createTime", FieldFill.INSERT))
                            .addTableFills(new Property("updateUser", FieldFill.INSERT_UPDATE))
                            .addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))

                            // service
                            .serviceBuilder()
                            .convertServiceFileName(entityName -> entityName + ConstVal.SERVICE)

                            // controller
                            .controllerBuilder()
                            .enableRestStyle()
                    ;

                    if (override) {
                        builder.controllerBuilder()
                                .enableFileOverride()
                                .serviceBuilder()
                                .enableFileOverride()
                                .entityBuilder()
                                .enableFileOverride()
                                .mapperBuilder()
                                .enableFileOverride();
                    }

                })
                .templateConfig(builder -> {
                    if (!outputFiles.contains(1)) {
                        builder.disable(TemplateType.CONTROLLER);
                    }
                    if (!outputFiles.contains(2)) {
                        builder.disable(TemplateType.SERVICE);
                    }
                    if (!outputFiles.contains(3)) {
                        builder.disable(TemplateType.SERVICE_IMPL);
                    }
                    if (!outputFiles.contains(4)) {
                        builder.disable(TemplateType.MAPPER);
                    }
                    if (!outputFiles.contains(5)) {
                        builder.disable(TemplateType.XML);
                    }
                    if (!outputFiles.contains(6)) {
                        builder.disable(TemplateType.ENTITY);
                    }
                })
                .injectionConfig(builder -> {
                    builder.beforeOutputFile((tableInfo, objectMap) -> {
                        Map<String, Boolean> controllerMethods = new HashMap<>();
                        List<Integer> createMethods = genCodeVo.getCreateMethods();
                        if (!createMethods.isEmpty()) {
                            controllerMethods.put("hasMethod", true);
                        }
                        for (Integer createMethod : createMethods) {
                            switch (createMethod) {
                                case 1 -> controllerMethods.put("list", true);
                                case 2 -> controllerMethods.put("page", true);
                                case 3 -> controllerMethods.put("detail", true);
                                case 4 -> controllerMethods.put("create", true);
                                case 5 -> controllerMethods.put("update", true);
                                case 6 -> controllerMethods.put("delete", true);
                            }
                        }

                        objectMap.put("entityPath", join(config.getParent(), moduleName, "model"));
                        objectMap.put("daoPath", join(config.getParent(), moduleName, "dao"));
                        objectMap.put("tablePrefix", tablePrefix);
                        objectMap.put("tableSuffix", tableSuffix);
                        objectMap.put("controllerMethods", controllerMethods);
                    });

                    if (outputFiles.contains(6)) {
                        builder.customFile(getTableCustomFile(override));
                    }
                    if (outputFiles.contains(7)) {
                        builder.customFile(getSelectDtoCustomFile(override));
                    }
                    if (outputFiles.contains(8)) {
                        builder.customFile(getVoCustomFile(override));
                    }
                    if (outputFiles.contains(9)) {
                        builder.customFile(getDtoCustomFile(override));
                    }
                    if (outputFiles.contains(10)) {
                        builder.customFile(getJsFile(tablePrefix, tableSuffix, override));
                    }
                    if (outputFiles.contains(11)) {
                        builder.customFile(getIndexVueFile(tablePrefix, tableSuffix, override));
                    }
                    builder.customFile(getDaoCustomFile(override));
                })
                .execute();
    }

    private CustomFile getSelectDtoCustomFile(boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName("SelectDto.java")
                .templatePath("/templates/selectDto.java.btl")
                .packageName("model.dto");
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private CustomFile getVoCustomFile(boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName("Vo.java")
                .templatePath("/templates/vo.java.btl")
                .packageName("model.vo");
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private CustomFile getDtoCustomFile(boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName("Dto.java")
                .templatePath("/templates/dto.java.btl")
                .packageName("model.dto");
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private CustomFile getTableCustomFile(boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName("Table.java")
                .templatePath("/templates/table.java.btl")
                .packageName("model.table");
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private CustomFile getDaoCustomFile(boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName("Dao.java")
                .templatePath("/templates/dao.java.btl")
                .packageName("dao");
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private CustomFile getJsFile(String tablePrefix, String tableSuffix, boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName(".ts")
                .filePath("./jy-ui/src/api/business/")
                .formatNameFunction(item -> tableSuffix)
                .templatePath("/templates/vue/index.ts.blt")
                .packageName(tablePrefix);
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private CustomFile getIndexVueFile(String tablePrefix, String tableSuffix, boolean override) {
        CustomFile.Builder builder = new CustomFile.Builder()
                .fileName("index.vue")
                .filePath("./jy-ui/src/views/business/")
                .formatNameFunction(item -> tableSuffix + "/")
                .templatePath("/templates/vue/index.vue.blt")
                .packageName(tablePrefix);
        if (override) {
            builder.enableFileOverride();
        }
        return builder.build();
    }

    private String join(String... str) {
        StringJoiner joiner = new StringJoiner(".");
        for (String item : str) {
            if (StrUtil.isNotBlank(item)) {
                joiner.add(item);
            }
        }
        return joiner.toString();
    }

}
