package org.springblade.common.generator;

/**
 * Author pangqijun
 * Date 2023/2/7
 * Description
 */
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.OracleTypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.SqlServerTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

@Data
public class BladeCodeGenerator {
    private static final Logger log = LoggerFactory.getLogger(BladeCodeGenerator.class);
    private String systemName = "sword";
    private String codeName;
    private String serviceName = "blade-service";
    private String packageName = "org.springblade.test";
    private String packageDir;
    private String packageWebDir;
    private String[] tablePrefix = new String[]{"blade_"};
    private String[] includeTables = new String[]{"blade_test"};
    private String[] excludeTables = new String[0];
    private Boolean hasSuperEntity;
    private Boolean hasWrapper;
    private String[] superEntityColumns;
    private String tenantColumn;
    private Boolean isSwagger2;
    private String driverName;
    private String url;
    private String username;
    private String password;

    public void run() {
        Properties props = this.getProperties();
        AutoGenerator mpg = new AutoGenerator();
        GlobalConfig gc = new GlobalConfig();
        String outputDir = this.getOutputDir();
        String author = props.getProperty("author");
        gc.setOutputDir(outputDir);
        gc.setAuthor(author);
        gc.setFileOverride(true);
        gc.setOpen(false);
        gc.setActiveRecord(false);
        gc.setEnableCache(false);
        gc.setBaseResultMap(true);
        gc.setBaseColumnList(true);
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setServiceName("I%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setControllerName("%sController");
        gc.setSwagger2(this.isSwagger2);
        mpg.setGlobalConfig(gc);
        DataSourceConfig dsc = new DataSourceConfig();
        String driverName = Func.toStr(this.driverName, props.getProperty("spring.datasource.driver-class-name"));
        if (StringUtil.containsAny(driverName, new CharSequence[]{DbType.MYSQL.getDb()})) {
            dsc.setDbType(DbType.MYSQL);
            dsc.setTypeConvert(new MySqlTypeConvert());
        } else if (StringUtil.containsAny(driverName, new CharSequence[]{DbType.POSTGRE_SQL.getDb()})) {
            dsc.setDbType(DbType.POSTGRE_SQL);
            dsc.setTypeConvert(new PostgreSqlTypeConvert());
        } else if (StringUtil.containsAny(driverName, new CharSequence[]{DbType.SQL_SERVER.getDb()})) {
            dsc.setDbType(DbType.SQL_SERVER);
            dsc.setTypeConvert(new SqlServerTypeConvert());
        } else {
            dsc.setDbType(DbType.ORACLE);
            dsc.setTypeConvert(new OracleTypeConvert());
        }

        dsc.setDriverName(driverName);
        dsc.setUrl(Func.toStr(this.url, props.getProperty("spring.datasource.url")));
        dsc.setUsername(Func.toStr(this.username, props.getProperty("spring.datasource.username")));
        dsc.setPassword(Func.toStr(this.password, props.getProperty("spring.datasource.password")));
        mpg.setDataSource(dsc);
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setTablePrefix(this.tablePrefix);
        if (this.includeTables.length > 0) {
            strategy.setInclude(this.includeTables);
        }

        if (this.excludeTables.length > 0) {
            strategy.setExclude(this.excludeTables);
        }

        if (this.hasSuperEntity) {
            strategy.setSuperEntityClass("org.springblade.common.base.BaseEntity");
            strategy.setSuperEntityColumns(this.superEntityColumns);
            strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
            strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
        } else {
            strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
            strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
        }

        strategy.setSuperControllerClass("org.springblade.core.boot.ctrl.BladeController");
        strategy.setEntityBuilderModel(false);
        strategy.setEntityLombokModel(true);
        strategy.setControllerMappingHyphenStyle(true);
        mpg.setStrategy(strategy);
        PackageConfig pc = new PackageConfig();
        pc.setModuleName((String) null);
        pc.setParent(this.packageName);
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setXml("mapper");
        mpg.setPackageInfo(pc);
        mpg.setCfg(this.getInjectionConfig());
        mpg.execute();
    }

    private InjectionConfig getInjectionConfig() {
        final String servicePackage = this.serviceName.split("-").length > 1 ? this.serviceName.split("-")[1] : this.serviceName;
        final Map<String, Object> map = new HashMap(16);
        InjectionConfig cfg = new InjectionConfig() {
            public void initMap() {
                map.put("codeName", BladeCodeGenerator.this.codeName);
                map.put("serviceName", BladeCodeGenerator.this.serviceName);
                map.put("servicePackage", servicePackage);
                map.put("servicePackageLowerCase", servicePackage.toLowerCase());
                map.put("tenantColumn", BladeCodeGenerator.this.tenantColumn);
                map.put("hasWrapper", BladeCodeGenerator.this.hasWrapper);
                this.setMap(map);
            }
        };
        List<FileOutConfig> focList = new ArrayList();
        String sqlPath = "/templates/sql/menu.sql.vm";
        if (Func.equals(this.systemName, "lemon")) {
            sqlPath = "/templates/lemon/menu.sql.vm";
        }

        focList.add(new FileOutConfig(sqlPath) {
            public String outputFile(TableInfo tableInfo) {
                map.put("entityKey", tableInfo.getEntityName().toLowerCase());
                map.put("menuId", IdWorker.getId());
                map.put("addMenuId", IdWorker.getId());
                map.put("editMenuId", IdWorker.getId());
                map.put("removeMenuId", IdWorker.getId());
                map.put("viewMenuId", IdWorker.getId());
                return BladeCodeGenerator.this.getOutputDir() + "//sql/" + tableInfo.getEntityName().toLowerCase() + ".menu.mysql";
            }
        });
        focList.add(new FileOutConfig("/templates/entityVO.java.vm") {
            public String outputFile(TableInfo tableInfo) {
                return BladeCodeGenerator.this.getOutputDir() + "/" + BladeCodeGenerator.this.packageName.replace(".", "/") + "/vo/" + tableInfo.getEntityName() + "VO" + ".java";
            }
        });
        focList.add(new FileOutConfig("/templates/entityDTO.java.vm") {
            public String outputFile(TableInfo tableInfo) {
                return BladeCodeGenerator.this.getOutputDir() + "/" + BladeCodeGenerator.this.packageName.replace(".", "/") + "/dto/" + tableInfo.getEntityName() + "DTO" + ".java";
            }
        });
        if (this.hasWrapper) {
            focList.add(new FileOutConfig("/templates/wrapper.java.vm") {
                public String outputFile(TableInfo tableInfo) {
                    return BladeCodeGenerator.this.getOutputDir() + "/" + BladeCodeGenerator.this.packageName.replace(".", "/") + "/wrapper/" + tableInfo.getEntityName() + "Wrapper" + ".java";
                }
            });
        }

        if (Func.isNotBlank(this.packageWebDir)) {
            if (Func.equals(this.systemName, "sword")) {
                focList.add(new FileOutConfig("/templates/sword/action.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/actions/" + tableInfo.getEntityName().toLowerCase() + ".js";
                    }
                });
                focList.add(new FileOutConfig("/templates/sword/model.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/models/" + tableInfo.getEntityName().toLowerCase() + ".js";
                    }
                });
                focList.add(new FileOutConfig("/templates/sword/service.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/services/" + tableInfo.getEntityName().toLowerCase() + ".js";
                    }
                });
                focList.add(new FileOutConfig("/templates/sword/list.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/pages/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + ".js";
                    }
                });
                focList.add(new FileOutConfig("/templates/sword/add.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/pages/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + "Add.js";
                    }
                });
                focList.add(new FileOutConfig("/templates/sword/edit.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/pages/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + "Edit.js";
                    }
                });
                focList.add(new FileOutConfig("/templates/sword/view.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/pages/" + StringUtil.firstCharToUpper(servicePackage) + "/" + tableInfo.getEntityName() + "/" + tableInfo.getEntityName() + "View.js";
                    }
                });
            } else if (Func.equals(this.systemName, "saber")) {
                focList.add(new FileOutConfig("/templates/saber/api.js.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/api/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + ".js";
                    }
                });
                focList.add(new FileOutConfig("/templates/saber/crud.vue.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/views/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + ".vue";
                    }
                });
            } else if (Func.equals(this.systemName, "lemon")) {
                focList.add(new FileOutConfig("/templates/lemon/data.ts.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/api/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + ".ts";
                    }
                });
                focList.add(new FileOutConfig("/templates/lemon/data.data.ts.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/views/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + ".data.ts";
                    }
                });
                focList.add(new FileOutConfig("/templates/lemon/index.vue.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/views/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + "/index.vue";
                    }
                });
                focList.add(new FileOutConfig("/templates/lemon/Modal.vue.vm") {
                    public String outputFile(TableInfo tableInfo) {
                        return BladeCodeGenerator.this.getOutputWebDir() + "/views/" + servicePackage.toLowerCase() + "/" + tableInfo.getEntityName().toLowerCase() + "/" + tableInfo.getEntityName() + "Modal.vue";
                    }
                });
            }
        }

        cfg.setFileOutConfigList(focList);
        return cfg;
    }

    private Properties getProperties() {
        Resource resource = new ClassPathResource("/templates/code.properties");
        Properties props = new Properties();

        try {
            props = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException var4) {
            var4.printStackTrace();
        }

        return props;
    }

    public String getOutputDir() {
        return (Func.isBlank(this.packageDir) ? System.getProperty("user.dir") + "/blade-ops/blade-develop" : this.packageDir) + "/src/main/java";
    }

    public String getOutputWebDir() {
        return (Func.isBlank(this.packageWebDir) ? System.getProperty("user.dir") : this.packageWebDir) + "/src";
    }

    private String getGeneratorViewPath(String viewOutputDir, TableInfo tableInfo, String suffixPath) {
        String name = StringUtils.firstToLowerCase(tableInfo.getEntityName());
        String path = viewOutputDir + "/" + name + "/" + name + suffixPath;
        File viewDir = (new File(path)).getParentFile();
        if (!viewDir.exists()) {
            viewDir.mkdirs();
        }

        return path;
    }

    public BladeCodeGenerator() {
        this.hasSuperEntity = Boolean.FALSE;
        this.hasWrapper = Boolean.FALSE;
        this.superEntityColumns = new String[]{"id", "create_user_id", "update_user_id", "create_time", "update_time", "is_delete"};
        this.tenantColumn = "tenant_id";
        this.isSwagger2 = Boolean.TRUE;
    }
}
