package com.xjrsoft.module.generator.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import cn.hutool.db.meta.MetaUtil;
import cn.hutool.db.meta.Table;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.utils.JdbcToJavaUtil;
import com.xjrsoft.module.generator.constant.EntityConstant;
import com.xjrsoft.module.generator.dto.ApiGenerateCodesDto;
import com.xjrsoft.module.generator.entity.FieldConfig;
import com.xjrsoft.module.generator.entity.GeneratorConfig;
import com.xjrsoft.module.generator.entity.TableConfig;
import com.xjrsoft.module.generator.service.IApiGeneratorService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ApiGeneratorServiceImpl implements IApiGeneratorService {

    private static final String TEMPLATE_PATH = "src/main/resources/apiTemplates";

    private static final String ENTITY_TEMPLATE_NAME = "entity.java.ftl";

    private static final String ADD_DTO_TEMPLATE_NAME = "add.dto.java.ftl";

    private static final String UPDATE_DTO_TEMPLATE_NAME = "update.dto.java.ftl";

    private static final String PAGE_DTO_TEMPLATE_NAME = "page.list.dto.java.ftl";

    private static final String PAGE_VO_TEMPLATE_NAME = "page.list.vo.java.ftl";

    private static final String INFO_VO_TEMPLATE_NAME = "vo.java.ftl";

    private static final String MAPPPER_TEMPLATE_NAME = "mapper.java.ftl";

    private static final String SERVICE_TEMPLATE_NAME = "service.java.ftl";

    private static final String SERVICE_IMPL_TEMPLATE_NAME = "service.impl.java.ftl";

    private static final String CONTROLLER_TEMPLATE_NAME = "controller.java.ftl";

    private Configuration cfg;

    /**
     * 生成代码
     *
     * @return
     */
    @Override
    public Boolean generateCodes(ApiGenerateCodesDto dto) throws IOException {
        //主表
        TableConfig mainTable = getMainTable(dto.getTableConfigs());

        cfg = new Configuration();
        cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));
        cfg.setDefaultEncoding("UTF-8");

        GeneratorConfig generatorConfig = new GeneratorConfig();
        generatorConfig.setTableConfigs(dto.getTableConfigs());

        List<Table> tableInfos = getTableInfos(dto.getDs(), generatorConfig);


        //---------------------------------------生成entity开始----------------------------------------------------
        {
            Map<String, String> entityCodeMap = getEntityCode(dto, tableInfos);

            for (Map.Entry<String, String> entry : entityCodeMap.entrySet()) {
                writeFile(getEntityOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }
        //---------------------------------------生成dto开始---------------------------------------
        {
            Map<String, String> dtoMap = new HashMap<>(3);
            dtoMap.putAll(getAddDtoCode(dto, tableInfos));
            dtoMap.putAll(getUpdateDtoCode(dto, tableInfos));
            dtoMap.putAll(getPageDtoCode(dto, tableInfos, mainTable));

            for (Map.Entry<String, String> entry : dtoMap.entrySet()) {
                writeFile(getDtoOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }
        //---------------------------------------生成Vo开始---------------------------------------
        {
            Map<String, String> voMap = new HashMap<>(2);
            voMap.putAll(getPageVoCode(dto, tableInfos, mainTable));
            voMap.putAll(getInfoVoCode(dto, tableInfos, mainTable));

            for (Map.Entry<String, String> entry : voMap.entrySet()) {
                writeFile(getVoOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        //---------------------------------------生成三层代码开始----------------------------------------------------
        {
            Map<String, String> mapperCode = getMapperCode(dto, tableInfos);

            for (Map.Entry<String, String> entry : mapperCode.entrySet()) {
                writeFile(getMapperOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        {
            Map<String, String> serviceCode = getServiceCode(dto, tableInfos, mainTable, tableInfos.size() > 1);

            for (Map.Entry<String, String> entry : serviceCode.entrySet()) {
                writeFile(getServiceOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        {
            Map<String, String> serviceImplCode = getServiceImplCode(dto, tableInfos, mainTable, tableInfos.size() > 1);
            for (Map.Entry<String, String> entry : serviceImplCode.entrySet()) {
                writeFile(getServiceImplOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        {
            Map<String, String> controllerCode = getControllerCode(dto, tableInfos, mainTable);
            for (Map.Entry<String, String> entry : controllerCode.entrySet()) {
                writeFile(getControllerOutputDir(dto), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        return null;
    }

    /**
     * 根据配置生成实体类代码
     *
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getEntityCode(ApiGenerateCodesDto dto, List<Table> tableInfos) {
        Map<String, String> entityCodeMap = new HashMap<>(tableInfos.size());
        // 遍历数据表配置 根据表明搜索表信息  然后生成代码
        for (Table tableInfo : tableInfos) {
            //获取表的所有字段
            Collection<Column> columns = tableInfo.getColumns();
            List<FieldConfig> fieldConfigList = new ArrayList<>();

            for (Column column : columns) {
                FieldConfig fieldConfig = new FieldConfig();

                if (GlobalConstant.AUTO_INSERT.contains(column.getName())) {
                    fieldConfig.setAutoInsert(true);
                }

                if (GlobalConstant.AUTO_UPDATE.contains(column.getName())) {
                    fieldConfig.setAutoUpdate(true);
                }

                if (StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK)) {
                    fieldConfig.setDeleteMark(true);
                }
                fieldConfig.setFieldComment(column.getComment());
                fieldConfig.setPk(column.isPk());
                fieldConfig.setFieldType(JdbcToJavaUtil.getClassName(column.getTypeEnum(),1));
                fieldConfig.setFieldName(StrUtil.toCamelCase(column.getName()));

                fieldConfigList.add(fieldConfig);

            }


            //如果有子表  每多一个子表  多加1个长度
            Map<String, Object> entityMap = new HashMap<>(8);
            entityMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "entity");
            entityMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
            entityMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            entityMap.put(EntityConstant.TABLE_NAME, tableInfo.getTableName());
            entityMap.put(EntityConstant.TABLE_COMMENT, tableInfo.getComment());
            //先将表明转换为驼峰命名 然后在转换为首字母大写
            entityMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())));
            entityMap.put(EntityConstant.TABLE_FIELDS, fieldConfigList);

            //如果是多表关联 则需要添加关联表的信息  判断数据表配置是否大于1  并且  是主表
            boolean isMainTable = dto.getTableConfigs().stream().anyMatch(x -> StrUtil.equals(x.getTableName(), tableInfo.getTableName()) && x.getIsMain());
            if (tableInfos.size() > 1 && isMainTable) {
                List<TableConfig> childTables = dto.getTableConfigs().stream()
                        .filter(x -> !x.getIsMain())
                        .collect(Collectors.toList());

                List<TableConfig> newChildTables = new ArrayList<>();
                for (TableConfig childTable : childTables) {
                    TableConfig newTableConfig = BeanUtil.toBean(childTable, TableConfig.class);

                    newTableConfig.setRelationField(StrUtil.toCamelCase(childTable.getRelationField()));
                    newTableConfig.setRelationTableField(StrUtil.toCamelCase(childTable.getRelationTableField()));
                    newTableConfig.setTableName(StrUtil.toCamelCase(childTable.getTableName()));

                    newChildTables.add(newTableConfig);
                }
//                Optional<TableConfig> parentTable = generatorConfig.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
                entityMap.put(EntityConstant.CHILD_TABLES, newChildTables);
//                parentTable.ifPresent(x -> entityMap.put(EntityConstant.PARENT_RELATION_FIELD_KEY, x.getRelationTableField()));
            }

            Template template = cfg.getTemplate(ENTITY_TEMPLATE_NAME);
            String entityContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, entityMap);

            entityCodeMap.put(StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())), entityContent);
        }

        return entityCodeMap;
    }

    /**
     * 根据配置生成mapper文件
     *
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getMapperCode(ApiGenerateCodesDto dto, List<Table> tableInfos) {


        Map<String, String> map = new HashMap<>(1 + tableInfos.size() - 1);

        for (Table tableInfo : tableInfos) {

            Map<String, Object> mapperMap = new HashMap<>(7);
            mapperMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "mapper");
            mapperMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
            mapperMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            mapperMap.put(EntityConstant.TABLE_COMMENT, tableInfo.getComment());
            //先将表明转换为驼峰命名 然后在转换为首字母大写
            mapperMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())));
//            mapperMap.put(EntityConstant.FUNCTION_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(generatorConfig.getOutputConfig().getClassName())));
            mapperMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());

            mapperMap.put(EntityConstant.IS_MULTI, tableInfos.size() > 1);

            Template template = cfg.getTemplate(MAPPPER_TEMPLATE_NAME);

            String mapperContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, mapperMap);

            map.put(StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())) + "Mapper", mapperContent);

        }


        return map;
    }

    /**
     * 根据配置生成service文件
     *
     * @param mainTableConfig 主表配置
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getServiceCode(ApiGenerateCodesDto dto, List<Table> tableInfos, TableConfig mainTableConfig, Boolean isMulti) {
        Optional<Table> mainTable = tableInfos.stream().filter(x -> x.getTableName().equals(mainTableConfig.getTableName())).findFirst();
        if (!mainTable.isPresent()) {
            throw new MyException("未设置主表！");
        }

        Map<String, Object> serviceMap = new HashMap<>(7);
        serviceMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "service");
        serviceMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
        serviceMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        serviceMap.put(EntityConstant.TABLE_COMMENT, mainTable.get().getComment());
        //先将表明转换为驼峰命名 然后在转换为首字母大写
        serviceMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));
        serviceMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());
        serviceMap.put(EntityConstant.FUNCTION_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));
        serviceMap.put(EntityConstant.IS_MULTI, isMulti);

        Template template = cfg.getTemplate(SERVICE_TEMPLATE_NAME);

        String serviceContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, serviceMap);

        Map<String, String> map = new HashMap<>(1);
        map.put("I" + StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())) + "Service", serviceContent);
        return map;
    }

    /**
     * 根据配置生成service impl代码
     *
     * @param mainTableConfig 主表配置
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getServiceImplCode(ApiGenerateCodesDto dto, List<Table> tableInfos, TableConfig mainTableConfig, Boolean isMulti) {
        Optional<TableConfig> first = dto.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
        if (!first.isPresent()) {
            throw new MyException("未设置主表！");
        }
        Optional<Table> mainTable = tableInfos.stream().filter(x -> x.getTableName().equals(mainTableConfig.getTableName())).findFirst();
        if (!mainTable.isPresent()) {
            throw new MyException("未设置主表！");
        }

        Map<String, Object> serviceMap = new HashMap<>(8);
        serviceMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "service.impl");
        serviceMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
        serviceMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        serviceMap.put(EntityConstant.TABLE_COMMENT, mainTable.get().getComment());
        serviceMap.put(EntityConstant.PARENT_TABLE_KEY, first.get().getPkField());
        //先将表明转换为驼峰命名 然后在转换为首字母大写
        serviceMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));
        serviceMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());
        serviceMap.put(EntityConstant.FUNCTION_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));

        serviceMap.put(EntityConstant.IS_MULTI, isMulti);
        serviceMap.put(EntityConstant.DATASOURCE_ID, "master");

        //传入所有子表
        List<TableConfig> childTables = dto.getTableConfigs().stream().filter(x -> !x.getIsMain()).collect(Collectors.toList());

        List<TableConfig> newChildTables = new ArrayList<>();
        for (TableConfig childTable : childTables) {
            TableConfig newTableConfig = BeanUtil.toBean(childTable, TableConfig.class);

            newTableConfig.setRelationField(StrUtil.toCamelCase(childTable.getRelationField()));
            newTableConfig.setRelationTableField(StrUtil.toCamelCase(childTable.getRelationTableField()));
            newTableConfig.setTableName(StrUtil.toCamelCase(childTable.getTableName()));
            newTableConfig.setPkField(StrUtil.toCamelCase(childTable.getPkField()));
            newTableConfig.setPkType(childTable.getPkType());

            newChildTables.add(newTableConfig);
        }

        serviceMap.put(EntityConstant.CHILD_TABLES, newChildTables);

        Template template = cfg.getTemplate(SERVICE_IMPL_TEMPLATE_NAME);

        String serviceImplContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, serviceMap);

        Map<String, String> map = new HashMap<>(1);
        map.put(StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())) + "ServiceImpl", serviceImplContent);
        return map;
    }

    /**
     * 根据配置生成控制器代码
     *
     * @param mainTableConfig 主表配置
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getControllerCode(ApiGenerateCodesDto dto, List<Table> tableInfos, TableConfig mainTableConfig) {
        List<FieldConfig> queryFieldConfigList = new ArrayList<>();

        Optional<Table> mainTable = tableInfos.stream().filter(x -> x.getTableName().equals(mainTableConfig.getTableName())).findFirst();
        if (!mainTable.isPresent()) {
            throw new MyException("未设置主表！");
        }

        Map<String, Object> controllerMap = new HashMap<>(12);
        controllerMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "controller");
        controllerMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
        controllerMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        controllerMap.put(EntityConstant.TABLE_COMMENT, mainTable.get().getComment());
        controllerMap.put(EntityConstant.IS_PAGE, dto.isPage());
        //先将表名转换为驼峰命名 然后在转换为首字母大写
        controllerMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));
        controllerMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());
        controllerMap.put(EntityConstant.TABLE_FIELDS, queryFieldConfigList);
        controllerMap.put(EntityConstant.PK_FIELD, StrUtil.toCamelCase(mainTableConfig.getPkField()));
        controllerMap.put(EntityConstant.IS_MULTI, tableInfos.size() > 1);
        controllerMap.put(EntityConstant.CODE_RULES, "");

        controllerMap.put(EntityConstant.FUNCTION_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));

        String orderType = "desc";
        String orderField = StrUtil.toCamelCase(mainTableConfig.getPkField());

        controllerMap.put(EntityConstant.ORDER_TYPE, StrUtil.equalsIgnoreCase(orderType, "desc"));
        controllerMap.put(EntityConstant.ORDER_FIELD, StrUtil.upperFirst(orderField));
        controllerMap.put(EntityConstant.IS_DATA_AUTH, false);
        // 导入导出
        controllerMap.put(EntityConstant.IS_IMPORT, dto.isImport());
        controllerMap.put(EntityConstant.IS_EXPORT, dto.isExport());

        Template template = cfg.getTemplate(CONTROLLER_TEMPLATE_NAME);

        String controllerContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, controllerMap);

        Map<String, String> map = new HashMap<>(1);
        map.put(StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())) + "Controller", controllerContent);
        return map;
    }

    /**
     * 根据配置新增dto代码
     *
     * @param tableInfos 表信息
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getAddDtoCode(ApiGenerateCodesDto dto, List<Table> tableInfos) {
        Map<String, String> addCodeMap = new HashMap<>(tableInfos.size());
        // 遍历数据表配置 根据表明搜索表信息  然后生成代码
        for (Table tableInfo : tableInfos) {
            //获取表的所有字段
            Collection<Column> columnList = tableInfo.getColumns();

            List<FieldConfig> fieldConfigList = new ArrayList<>();

            for (Column column : columnList) {
                FieldConfig fieldConfig = new FieldConfig();

                //因为是新增Dto  不需要主键
                if (column.isPk()) {
                    continue;
                }

                //不属于 固定的审计字段 都需要生成
                if (
                        !GlobalConstant.AUTO_INSERT.contains(column.getName()) &&
                                !GlobalConstant.AUTO_UPDATE.contains(column.getName()) &&
                                !StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK) &&
                                !StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK)
                ) {
                    String fieldType = JdbcToJavaUtil.getClassName(column.getTypeEnum(),1);
                    fieldConfig.setFieldComment(column.getComment());
                    fieldConfig.setPk(false);
                    fieldConfig.setFieldType(fieldType);
                    fieldConfig.setFieldName(StrUtil.toCamelCase(column.getName()));

                    fieldConfigList.add(fieldConfig);

                }

            }


            //如果有子表  每多一个子表  多加2个长度
            Map<String, Object> entityMap = new HashMap<>(8 + (tableInfos.size() - 1) * 2);
            entityMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "dto");
            entityMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
            entityMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            entityMap.put(EntityConstant.TABLE_NAME, tableInfo.getTableName());
            entityMap.put(EntityConstant.TABLE_COMMENT, tableInfo.getComment());
            //先将表明转换为驼峰命名 然后在转换为首字母大写
            entityMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())));
            entityMap.put(EntityConstant.TABLE_FIELDS, fieldConfigList);
            entityMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());

            //如果是多表关联 则需要添加关联表的信息  判断数据表配置是否大于1  并且  是主表
            boolean isMainTable = dto.getTableConfigs().stream().anyMatch(x -> StrUtil.equals(x.getTableName(), tableInfo.getTableName()) && x.getIsMain());
            if (tableInfos.size() > 1 && isMainTable) {
                List<TableConfig> childTables = dto.getTableConfigs().stream()
                        .filter(x -> !x.getIsMain())
                        .collect(Collectors.toList());
                List<TableConfig> newChildTables = new ArrayList<>();
                for (TableConfig childTable : childTables) {
                    TableConfig newTableConfig = BeanUtil.toBean(childTable, TableConfig.class);

                    newTableConfig.setRelationField(StrUtil.toCamelCase(childTable.getRelationField()));
                    newTableConfig.setRelationTableField(StrUtil.toCamelCase(childTable.getRelationTableField()));
                    newTableConfig.setTableName(StrUtil.toCamelCase(childTable.getTableName()));

                    newChildTables.add(newTableConfig);
                }
//                Optional<TableConfig> parentTable = generatorConfig.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
                entityMap.put(EntityConstant.CHILD_TABLES, newChildTables);
//                parentTable.ifPresent(x -> entityMap.put(EntityConstant.PARENT_RELATION_FIELD_KEY, x.getRelationTableField()));
            }


            //生产目标代码
            Template template = cfg.getTemplate(ADD_DTO_TEMPLATE_NAME);
            String entityContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, entityMap);

            addCodeMap.put("Add" + StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())) + "Dto", entityContent);
        }

        return addCodeMap;
    }

    /**
     * 根据配置修改dto代码
     *
     * @param tableInfos 表信息
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getUpdateDtoCode(ApiGenerateCodesDto dto, List<Table> tableInfos) {
        Optional<TableConfig> first = dto.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
        if (!first.isPresent()) {
            throw new MyException("未设置主表！");
        }

        Map<String, String> updateCodeMap = new HashMap<>(tableInfos.size());
        // 遍历数据表配置 根据表明搜索表信息  然后生成代码
        for (Table tableInfo : tableInfos) {

            if (!first.get().getTableName().equals(tableInfo.getTableName())) {
                continue;
            }

            //获取表的所有字段
            Collection<Column> columns = tableInfo.getColumns();
            List<FieldConfig> fieldConfigList = new ArrayList<>();

            for (Column column : columns) {
                FieldConfig fieldConfig = new FieldConfig();

                if (!column.isPk()) {
                    continue;
                }

                //不属于 固定的审计字段 都需要生成
                if (
                        !GlobalConstant.AUTO_INSERT.contains(column.getName()) &&
                                !GlobalConstant.AUTO_UPDATE.contains(column.getName()) &&
                                !StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK) &&
                                !StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK)
                ) {
                    String fieldType = JdbcToJavaUtil.getClassName(column.getTypeEnum(),1);
                    fieldConfig.setFieldComment(column.getComment());
                    fieldConfig.setPk(false);
                    fieldConfig.setFieldType(fieldType);
                    fieldConfig.setFieldName(StrUtil.toCamelCase(column.getName()));

                    fieldConfigList.add(fieldConfig);
                }
            }

            //如果有子表  每多一个子表  多加2个长度
            Map<String, Object> entityMap = new HashMap<>(8 + (tableInfos.size() - 1) * 2);
            entityMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "dto");
            entityMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
            entityMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            entityMap.put(EntityConstant.TABLE_NAME, tableInfo.getTableName());
            entityMap.put(EntityConstant.TABLE_COMMENT, tableInfo.getComment());
            //先将表明转换为驼峰命名 然后在转换为首字母大写
            entityMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())));
            entityMap.put(EntityConstant.TABLE_FIELDS, fieldConfigList);

            entityMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());

            //如果是多表关联 则需要添加关联表的信息  判断数据表配置是否大于1  并且  是主表
            boolean isMainTable = dto.getTableConfigs().stream().anyMatch(x -> StrUtil.equals(x.getTableName(), tableInfo.getTableName()) && x.getIsMain());
            if (tableInfos.size() > 1 && isMainTable) {
                List<TableConfig> childTables = dto.getTableConfigs().stream()
                        .filter(x -> !x.getIsMain())
                        .collect(Collectors.toList());
                List<TableConfig> newChildTables = new ArrayList<>();
                for (TableConfig childTable : childTables) {
                    TableConfig newTableConfig = BeanUtil.toBean(childTable, TableConfig.class);

                    newTableConfig.setRelationField(StrUtil.toCamelCase(childTable.getRelationField()));
                    newTableConfig.setRelationTableField(StrUtil.toCamelCase(childTable.getRelationTableField()));
                    newTableConfig.setTableName(StrUtil.toCamelCase(childTable.getTableName()));

                    newChildTables.add(newTableConfig);
                }
//                Optional<TableConfig> parentTable = generatorConfig.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
                entityMap.put(EntityConstant.CHILD_TABLES, newChildTables);
//                parentTable.ifPresent(x -> entityMap.put(EntityConstant.PARENT_RELATION_FIELD_KEY, x.getRelationTableField()));
            }

            //生产目标代码
            Template template = cfg.getTemplate(UPDATE_DTO_TEMPLATE_NAME);
            String entityContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, entityMap);

            updateCodeMap.put("Update" + StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())) + "Dto", entityContent);
        }

        return updateCodeMap;
    }

    /**
     * 根据配置生成 表单Vo代码
     *
     * @param tableInfos      表信息
     * @param mainTableConfig 主表配置
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getInfoVoCode(ApiGenerateCodesDto dto, List<Table> tableInfos, TableConfig mainTableConfig) {
        //获取表的所有字段
        Optional<Table> mainTableOp = tableInfos.stream().filter(x -> StrUtil.equalsIgnoreCase(x.getTableName(), mainTableConfig.getTableName())).findFirst();

        Table mainTable;
        if (mainTableOp.isPresent()) {
            mainTable = mainTableOp.get();
        } else {
            throw new MyException("没有主表");
        }

        Map<String, String> infoVoMap = new HashMap<>(1);
        // 遍历数据表配置 根据表明搜索表信息  然后生成代码
        for (Table tableInfo : tableInfos) {
            //获取表的所有字段
            Collection<Column> columns = tableInfo.getColumns();


            List<FieldConfig> fieldConfigList = new ArrayList<>();

            for (Column column : columns) {
                FieldConfig fieldConfig = new FieldConfig();

                //只要不是审计字段都返回去
                if (!GlobalConstant.AUTO_INSERT.contains(column.getName()) &&
                        !GlobalConstant.AUTO_UPDATE.contains(column.getName()) &&
                        !StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK) &&
                        !StrUtil.equalsIgnoreCase(column.getName(), GlobalConstant.DELETE_MARK)) {

                    fieldConfig.setFieldComment(column.getComment());
                    fieldConfig.setPk(false);
                    fieldConfig.setFieldType(JdbcToJavaUtil.getClassName(column.getTypeEnum(),1));
                    fieldConfig.setFieldName(StrUtil.toCamelCase(column.getName()));

                    fieldConfigList.add(fieldConfig);

                }
            }


            Map<String, Object> columnMap = new HashMap<>(7 + (tableInfos.size() - 1) * 1);
            columnMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "vo");
            columnMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
            columnMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            columnMap.put(EntityConstant.TABLE_COMMENT, tableInfo.getComment() + "表单出参");
            //先将表明转换为驼峰命名 然后在转换为首字母大写
            columnMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())));
            columnMap.put(EntityConstant.TABLE_FIELDS, fieldConfigList);

            columnMap.put(EntityConstant.OUTPUT_AREA, dto.getPackageName());

            //如果是多表关联 则需要添加关联表的信息  判断数据表配置是否大于1  并且  是主表
            boolean isMainTable = dto.getTableConfigs().stream().anyMatch(x -> StrUtil.equals(x.getTableName(), tableInfo.getTableName()) && x.getIsMain());
            if (tableInfos.size() > 1 && isMainTable) {
                List<TableConfig> childTables = dto.getTableConfigs().stream()
                        .filter(x -> !x.getIsMain())
                        .collect(Collectors.toList());
                List<TableConfig> newChildTables = new ArrayList<>();
                for (TableConfig childTable : childTables) {
                    TableConfig newTableConfig = BeanUtil.toBean(childTable, TableConfig.class);

                    newTableConfig.setRelationField(StrUtil.toCamelCase(childTable.getRelationField()));
                    newTableConfig.setRelationTableField(StrUtil.toCamelCase(childTable.getRelationTableField()));
                    newTableConfig.setTableName(StrUtil.toCamelCase(childTable.getTableName()));

                    newChildTables.add(newTableConfig);
                }
//                Optional<TableConfig> parentTable = generatorConfig.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
                columnMap.put(EntityConstant.CHILD_TABLES, newChildTables);
//                parentTable.ifPresent(x -> columnMap.put(EntityConstant.PARENT_RELATION_FIELD_KEY, x.getRelationTableField()));
            }
            //生产目标代码

            Template template = cfg.getTemplate(INFO_VO_TEMPLATE_NAME);

            String infoVoConent = FreeMarkerTemplateUtils.processTemplateIntoString(template, columnMap);


            infoVoMap.put(StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getTableName())) + "Vo", infoVoConent);

        }

        return infoVoMap;
    }

    /**
     * 根据配置生成 分页入参dto代码
     *
     * @param tableInfos      表信息
     * @param mainTableConfig 主表配置
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getPageDtoCode(ApiGenerateCodesDto dto, List<Table> tableInfos, TableConfig mainTableConfig) {

        //获取表的所有字段
        Optional<Table> mainTableOp = tableInfos.stream().filter(x -> StrUtil.equalsIgnoreCase(x.getTableName(), mainTableConfig.getTableName())).findFirst();

        Table mainTable;
        if (mainTableOp.isPresent()) {
            mainTable = mainTableOp.get();
        } else {
            throw new MyException("没有主表");
        }


        List<FieldConfig> queryFieldConfigList = new ArrayList<>();

        Map<String, Object> queryMap = new HashMap<>(7);
        queryMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "dto");
        queryMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
        queryMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        queryMap.put(EntityConstant.TABLE_COMMENT, mainTable.getComment() + (dto.isPage() ? "分页查询入参" : "列表查询入参"));
        queryMap.put(EntityConstant.IS_PAGE, dto.isPage());
        //先将表明转换为驼峰命名 然后在转换为首字母大写
        queryMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())));
        queryMap.put(EntityConstant.TABLE_FIELDS, queryFieldConfigList);

        //生产目标代码
        Template template = cfg.getTemplate(PAGE_DTO_TEMPLATE_NAME);

        String pageDtoConent = FreeMarkerTemplateUtils.processTemplateIntoString(template, queryMap);

        Map<String, String> map = new HashMap<>(1);
        map.put(StrUtil.upperFirst(StrUtil.toCamelCase(mainTableConfig.getTableName())) + (dto.isPage() ? "Page" : "List") + "Dto", pageDtoConent);

        return map;
    }

    /**
     * 根据配置生成 列表页Vo代码
     *
     * @param tableInfos      表信息
     * @param mainTableConfig 主表配置
     * @return 代码
     */
    @SneakyThrows
    private Map<String, String> getPageVoCode(ApiGenerateCodesDto dto, List<Table> tableInfos, TableConfig mainTableConfig) {
        //获取表的所有字段
        Optional<Table> mainTableOp = tableInfos.stream().filter(x -> StrUtil.equalsIgnoreCase(x.getTableName(), mainTableConfig.getTableName())).findFirst();

        Table mainTable;
        if (mainTableOp.isPresent()) {
            mainTable = mainTableOp.get();
        } else {
            throw new MyException("没有主表");
        }

        Collection<Column> columns = mainTable.getColumns();


        List<FieldConfig> fieldConfigList = new ArrayList<>();

        for (Column column : columns) {

            FieldConfig fieldConfig = new FieldConfig();
            fieldConfig.setFieldComment(column.getComment());
            fieldConfig.setLabel(column.getComment());
            fieldConfig.setPk(false);
            //如果是主键 就默认使用字符串类型 前端无法识别long类型的精度
            fieldConfig.setFieldType(column.isPk() ? "String" : JdbcToJavaUtil.getClassName(column.getTypeEnum(),1));
            fieldConfig.setFieldName(StrUtil.toCamelCase(column.getName()));
            fieldConfigList.add(fieldConfig);
        }


        Map<String, Object> columnMap = new HashMap<>(7);
        columnMap.put(EntityConstant.PACKAGE, GlobalConstant.GENERATOR_DEFAULT_PATH + StringPool.DOT + dto.getPackageName() + StringPool.DOT + "vo");
        columnMap.put(EntityConstant.AUTH_NAME, dto.getAuthor());
        columnMap.put(EntityConstant.DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        columnMap.put(EntityConstant.TABLE_COMMENT, mainTable.getComment() + (dto.isPage() ? "分页列表出参" : "列表列表入参"));
        columnMap.put(EntityConstant.IS_PAGE, dto.isPage());
        //先将表明转换为驼峰命名 然后在转换为首字母大写
        columnMap.put(EntityConstant.ENTITY_CLASS_NAME, StrUtil.upperFirst(StrUtil.toCamelCase(mainTable.getTableName())));
        columnMap.put(EntityConstant.TABLE_FIELDS, fieldConfigList);
        // 导入导出
        columnMap.put(EntityConstant.IS_IMPORT, dto.isImport());
        columnMap.put(EntityConstant.IS_EXPORT, dto.isExport());


        //生产目标代码
        Template template = cfg.getTemplate(PAGE_VO_TEMPLATE_NAME);

        String pageVoConent = FreeMarkerTemplateUtils.processTemplateIntoString(template, columnMap);

        Map<String, String> map = new HashMap<>(1);
        map.put(StrUtil.upperFirst(StrUtil.toCamelCase(mainTable.getTableName())) + (dto.isPage() ? "Page" : "List") + "Vo", pageVoConent);

        return map;
    }

    /**
     * 获取表结构信息
     *
     * @return
     */
    private List<Table> getTableInfos(DataSource ds, GeneratorConfig generatorConfig) {
        List<Table> tableInfos = new ArrayList<>();
        for (TableConfig tableConfig : generatorConfig.getTableConfigs()) {
            tableInfos.add(MetaUtil.getTableMeta(ds, tableConfig.getTableName()));
        }
        return tableInfos;
    }

    private void writeFile(String dirPath, String fileName, String content) throws FileNotFoundException {
        File dir = new File(dirPath);
        if (!dir.exists()) {
            boolean mkdirs = dir.mkdirs();
        }
        String filePath = dirPath + StringPool.SLASH + fileName;
        File javaFile = new File(filePath);
        if (javaFile.exists()) {
            javaFile.renameTo(new File(dirPath + StringPool.SLASH + fileName + StringPool.DOT + System.currentTimeMillis() + ".bak"));
        }
        FileOutputStream voFile = new FileOutputStream(filePath);
        IoUtil.write(voFile, true, content.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 获取生成目录 默认生成到generator/main/java/com/xjrsoft/module
     *
     * @return
     */
    private String getOutputDir(ApiGenerateCodesDto dto) {
        if (dto.isOutMainDir()) {
            return System.getProperty("user.dir") + StringPool.SLASH + "src" + StringPool.SLASH + "main" + StringPool.SLASH + "java" + StringPool.SLASH + "com" + StringPool.SLASH + "xjrsoft" + StringPool.SLASH + "module";
        } else {
            return System.getProperty("user.dir") + StringPool.SLASH + "generator" + StringPool.SLASH + "main" + StringPool.SLASH + "java" + StringPool.SLASH + "com" + StringPool.SLASH + "xjrsoft" + StringPool.SLASH + "module";
        }
    }

    /**
     * 获取实体类生成目录
     *
     * @param dto
     * @return
     */
    private String getEntityOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "entity";
    }

    /**
     * 获取mapper的输出目录
     *
     * @param dto
     * @return
     */
    private String getMapperOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "mapper";
    }

    /**
     * 获取service的生成目录
     *
     * @param dto
     * @return
     */
    private String getServiceOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "service";
    }

    /**
     * 获取serviceImpl的生成目录
     *
     * @param dto
     * @return
     */
    private String getServiceImplOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "service" + StringPool.SLASH + "impl";
    }

    /**
     * 获取控制器生成目录
     *
     * @param dto
     * @return
     */
    private String getControllerOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "controller";
    }

    /**
     * 获取dto的生成目录
     *
     * @param dto
     * @return
     */
    private String getDtoOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "dto";
    }

    /**
     * 获取vo的输出目录
     *
     * @param dto
     * @return
     */
    private String getVoOutputDir(ApiGenerateCodesDto dto) {
        return getOutputDir(dto) + StringPool.SLASH + dto.getPackageName().toLowerCase() + StringPool.SLASH + "vo";
    }

    /**
     * 获取主表
     *
     * @param tableConfigs
     * @return
     */
    private TableConfig getMainTable(List<TableConfig> tableConfigs) {
        Optional<TableConfig> tableConfigOptional = tableConfigs.stream().filter(TableConfig::getIsMain).findFirst();
        //主表
        TableConfig mainTable;
        if (tableConfigOptional.isPresent()) {
            mainTable = tableConfigOptional.get();
        } else {
            throw new MyException("请选择主表");
        }
        return mainTable;
    }

}
