package com.codvision.base.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.codvision.base.config.EnhanceVelocityTemplateEngine;
import com.codvision.base.entity.GeneratorConfig;
import com.codvision.base.entity.vo.GeneratorConfigVO;
import com.codvision.base.mapper.GeneratorConfigMapper;
import com.codvision.base.service.IGeneratorConfigService;
import com.codvision.commoncore.exception.BusinessException;
import com.codvision.webcore.bean.BasicEntity;
import com.codvision.webcore.bean.QueryPage;
import com.codvision.webcore.service.IBaseService;
import com.codvision.webcore.service.impl.BaseServiceImpl;
import com.codvision.webcore.util.PageUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 生成代码配置 服务实现类
 * </p>
 *
 * @author lingee
 * @since 2023-11-03
 */
@Service
public class GeneratorConfigServiceImpl extends BaseServiceImpl<GeneratorConfigMapper, GeneratorConfig, GeneratorConfigVO>
        implements IGeneratorConfigService {

    @Autowired
    private DataSourceProperties dataSourceProperties;

    private DataSourceConfig.Builder datasourceBuilder;

    @PostConstruct
    private void initBuilder() {
        datasourceBuilder = new DataSourceConfig.Builder(
                dataSourceProperties.getUrl(),
                dataSourceProperties.getUsername(),
                dataSourceProperties.getPassword());
    }

    @Override
    public GeneratorConfigVO getDetail(Long id) {
        GeneratorConfigVO vo = new GeneratorConfigVO();
        GeneratorConfig po = this.getById(id);
        if (null != po) {
            BeanUtil.copyProperties(po, vo);
        }
        return vo;
    }

    @Override
    public IPage<GeneratorConfigVO> getPageResult(QueryPage<GeneratorConfig> queryPage) {
        QueryWrapper<GeneratorConfig> queryWrapper = queryPage.getWrapper();
        IPage<GeneratorConfigVO> pageResult = this.getBasePage(queryPage.getPage(), queryWrapper);
        return pageResult;
    }

    @Override
    public List<GeneratorConfigVO> listAll(GeneratorConfig queryBean) {
        QueryWrapper<GeneratorConfig> wrapper = PageUtil.initWrapper(queryBean);
        List<GeneratorConfig> list = this.list(wrapper);
        return BeanUtil.copyToList(list, GeneratorConfigVO.class);
    }

    @Override
    public void generateCode(Long id) {
        GeneratorConfig po = this.getById(id);
        if (ObjectUtils.isEmpty(po)) {
            throw new BusinessException("生成代码配置不存在" );
        }

        String outputDir = po.getExportPath();
        if (outputDir.lastIndexOf(File.separator) != outputDir.length() - 1) {
            outputDir += File.separator;
        }
        final String finalOutputDir = outputDir;
        FastAutoGenerator.create(datasourceBuilder)
                // 全局配置
                .globalConfig((scanner, builder) -> {
                    builder.author(po.getAuthor());
                    builder.outputDir(finalOutputDir + "/src/main/java" );
                    builder.disableOpenDir();
                    builder.enableSwagger();
                    builder.dateType(DateType.ONLY_DATE);
                })
                // 包配置
                .packageConfig((scanner, builder) -> {
                    builder.parent(po.getPackageName());
                    builder.controller("controller.manager" );
                    builder.pathInfo(Collections.singletonMap(OutputFile.xml, finalOutputDir + "/src/main/resources/mapper" ));
                })
                // 策略配置
                .strategyConfig((scanner, builder) -> {
                    builder.addInclude(getTables(po.getTables()))
                            .controllerBuilder().enableRestStyle().enableHyphenStyle()
                            .formatFileName("Manager%sController" )
                            .entityBuilder()
                            .enableLombok()
//                        .superClass(BaseEntity.class)
//                        .addSuperEntityColumns("id", "create_by", "create_by_name", "create_time", "update_by",
//                                "update_by_name", "update_time", "delete_flag")
//                        .addTableFills(new Property("createTime", FieldFill.INSERT),
//                                new Property("createByName", FieldFill.INSERT),
//                                new Property("createBy", FieldFill.INSERT),
//                                new Property("updateBy", FieldFill.UPDATE),
//                                new Property("updateByName", FieldFill.UPDATE),
//                                new Property("updateTime", FieldFill.INSERT_UPDATE),
//                                new Property("deleteFlag", FieldFill.INSERT))
                            .idType(IdType.AUTO)
                            .serviceBuilder()
                            .superServiceClass(IBaseService.class)
                            .superServiceImplClass(BaseServiceImpl.class)
                            .mapperBuilder()
                            .enableBaseResultMap();
                    if (StringUtils.isNotBlank(po.getTablePrefixName())) {
                        builder.addTablePrefix(po.getTablePrefixName());
                    }
                    if (po.getSuperEntity()) {
                        builder.entityBuilder()
                                .superClass(BasicEntity.class)
                                .addSuperEntityColumns("id" , "create_time" , "update_time" , "del_flag" );
                    }
                    if (po.getFileOverride()) {
                        builder.entityBuilder().enableFileOverride();
                        builder.serviceBuilder().enableFileOverride();
                        builder.controllerBuilder().enableFileOverride();
                        builder.mapperBuilder().enableFileOverride();
                    }
                    builder.build();
                })
                // 模板引擎配置，默认 Velocity 可选模板引擎 Beetl 或 Freemarker
                .templateConfig((scanner, builder) -> {
                    builder.controller("/template/controller.java.vm" );
                    builder.entity("/template/entity.java.vm" );
                    builder.service("/template/service.java.vm" );
                    builder.serviceImpl("/template/serviceImpl.java.vm" );
                })
                .injectionConfig((scanner, builder) -> {
                    builder.customFile(Collections.singletonMap("VO.java" , "template/vo.java.vm" ));
                })
                .templateEngine(new EnhanceVelocityTemplateEngine())
                .execute();
    }

    @Override
    public boolean save(GeneratorConfig generatorConfig) {
        Assert.notNull(generatorConfig, "生成代码配置不能为空" );
        getBaseMapper().insert(generatorConfig);
        return true;
    }

    @Override
    public boolean updateById(GeneratorConfig generatorConfig) {
        Assert.notNull(generatorConfig, "生成代码配置不能为空" );
        Assert.notNull(generatorConfig.getId(), "生成代码配置id不能为空" );
        getBaseMapper().updateById(generatorConfig);
        return true;
    }

    @Override
    public boolean removeById(Serializable id) {
        GeneratorConfig generatorConfig = getById(id);
        Assert.notNull(generatorConfig, "生成代码配置不存在" );
        getBaseMapper().deleteById(id);
        return true;
    }

    // 处理 all 情况

    /**
     * 分隔表名
     *
     * @param tables 表名
     * @return
     */
    private static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split("," ));
    }
}
