package com.mortals.xhx.base.system.gentable.service.impl;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.ParamDto;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.framework.util.EntityUtil;
import com.mortals.xhx.base.system.gentable.dao.GentableDao;
import com.mortals.xhx.base.system.gentable.model.*;
import com.mortals.xhx.base.system.gentable.service.GentableColumnService;
import com.mortals.xhx.base.system.gentable.service.GentableService;
import com.mortals.xhx.common.code.CacheServiceTypeEnum;
import com.mortals.xhx.common.code.DividedTableTypeEnum;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.key.Constant;
import com.mortals.xhx.common.key.GenConstants;
import com.mortals.xhx.common.utils.FreeMarkerUtils;
import com.mortals.xhx.common.utils.GenUtils;
import com.mortals.xhx.common.utils.ReadDoc;
import com.mortals.xhx.common.utils.StringUtils;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * <p>Title: 代码生成业务表</p>
 * <p>Description: GentableService service接口 </p>
 * <p>Copyright: Copyright &reg;  </p>
 *
 * @version 1.0.0
 */
@Service("gentableService")
public class GentableServiceImpl extends AbstractCRUDServiceImpl<GentableDao, GentableEntity, Long> implements GentableService {

    @Autowired
    private GentableColumnService gentableColumnService;

    @Override
    public List<GentableEntity> selectDbTableList(GentableQuery query) {
        //query to  dao
        ParamDto paramDto = new ParamDto();
        Map map = new HashMap();
        EntityUtil.entityToMap(query, map);
        paramDto.setCondition(map);
        return this.dao.selectDbTableList(paramDto);
    }

    @Override
    public List<GentableEntity> selectDbTableListByNames(String[] tableNames) {
        return this.getDao().selectDbTableListByNames(tableNames);
    }

    @Override
    public List<GentableEntity> selectGenTableAll() {
        return this.getDao().selectGenTableAll();
    }

    @Override
    public void importGenTable(List<GentableEntity> tableList, Context context) {
        String operName = context.getUser().getLoginName();
        try {
            for (GentableEntity table : tableList) {
                String tableName = table.getTableName();
                GenUtils.initTable(table, operName);
                table.setGenType(0);
                table.setGenPath("/");
                table.setTplCategory("crud");
                table.setCacheServiceType(CacheServiceTypeEnum.NORMAL_SERVICE.getValue());
                table.setDividedTableType(DividedTableTypeEnum.DIVIED_NO.getValue());
                //保存表
                this.save(table);
                //保存列
                List<GentableColumnEntity> genTableColumns = gentableColumnService.selectDbTableColumnsByName(tableName);
                for (GentableColumnEntity column : genTableColumns) {
                    GenUtils.initColumnField(column, table);
                    gentableColumnService.save(column);
                }
            }
        } catch (Exception e) {
            throw new AppException("导入失败：" + e.getMessage());
        }
    }

    @Override
    public void validData(GentableEntity genTable, Context context) throws AppException {
        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
            String options = JSON.toJSONString(genTable.getOptions());
            JSONObject paramsObj = JSONObject.parseObject(options);
            if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
                throw new AppException("树编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
                throw new AppException("树父编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
                throw new AppException("树名称字段不能为空");
            } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
                if (StringUtils.isEmpty(genTable.getSubTableName())) {
                    throw new AppException("关联子表的表名不能为空");
                } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) {
                    throw new AppException("子表关联的外键名不能为空");
                }
            }
        }
        super.validData(genTable, context);
    }

    @Override
    public Map<String, String> previewCode(Long id) {

        Map<String, String> dataMap = new LinkedHashMap<>();

        // 查询表信息
        GentableEntity gentableEntity = this.get(id);
        List<GentableColumnEntity> columnEntities = this.gentableColumnService.find(new GentableColumnQuery().tableId(gentableEntity.getId())).stream().map(column -> {
            //拆解枚举类，格式必须为（1.啊啊啊，2.不不不）
            String temp1 = StringUtils.substringBetween(column.getColumnComment(), "(", ")");
            String temp2 = StringUtils.substringBetween(column.getColumnComment(), "（", "）");
            GenUtils.checkDict(column, temp1);
            GenUtils.checkDict(column, temp2);
            return column;
        }).collect(Collectors.toList());
        gentableEntity.setColumns(columnEntities);
        // 设置主子表信息
        setSubTable(gentableEntity);
        // 设置主键列信息
        setPkColumn(gentableEntity);

        //生成模板基础数据
        Map data = FreeMarkerUtils.prepareSetFreeMarkerData(gentableEntity);
        //输出合成模板

        // 获取模板列表
        List<String> templates = FreeMarkerUtils.getTemplateList(gentableEntity.getTplCategory());
        for (String temp : templates) {
            try {
                //初始化freeMarkers
                FreeMarkerUtils.initFreeMarker(genFreePath(temp));
                // 渲染模板
                StringWriter sw = new StringWriter();
                Template template = FreeMarkerUtils.getTemplate(temp);
                template.process(data, sw);
                dataMap.put(temp, sw.toString());
            } catch (Exception e) {
                log.error("异常", e);
            }
        }
        return dataMap;

    }


    @Override
    protected void updateAfter(GentableEntity entity, Context context) throws AppException {
        if (!ObjectUtils.isEmpty(entity.getColumns())) {
            gentableColumnService.update(entity.getColumns());
        }
        super.updateAfter(entity, context);
    }


    @SneakyThrows
    @Override
    public byte[] downloadCode(String tableName) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        FreeMarkerUtils.menuSql = new StringBuilder();
        FreeMarkerUtils.dbSql = new StringBuilder();
        generatorCode(tableName, zip);
        if (StringUtils.isNotEmpty(FreeMarkerUtils.menuSql)) {
            zip.putNextEntry(new ZipEntry("db/menu.sql"));
            IOUtils.write(FreeMarkerUtils.menuSql.toString(), zip, Constant.UTF8);
        }
        if (StringUtils.isNotEmpty(FreeMarkerUtils.dbSql)) {
            zip.putNextEntry(new ZipEntry("db/db.sql"));
            IOUtils.write(FreeMarkerUtils.dbSql.toString(), zip, Constant.UTF8);
        }
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    @SneakyThrows
    @Override
    public byte[] downloadCode(String[] tableNames) {
        FreeMarkerUtils.menuSql = new StringBuilder();
        FreeMarkerUtils.dbSql = new StringBuilder();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames) {
            generatorCode(tableName, zip);
        }
        if (StringUtils.isNotEmpty(FreeMarkerUtils.menuSql)) {
            zip.putNextEntry(new ZipEntry("db/menu.sql"));
            IOUtils.write(FreeMarkerUtils.menuSql.toString(), zip, Constant.UTF8);
        }
        if (StringUtils.isNotEmpty(FreeMarkerUtils.dbSql)) {
            zip.putNextEntry(new ZipEntry("db/db.sql"));
            IOUtils.write(FreeMarkerUtils.dbSql.toString(), zip, Constant.UTF8);
        }

        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    @Override
    public void synchDb(String tableName) {
        GentableEntity table = this.selectOne(new GentableQuery().tableName(tableName));

        List<GentableColumnEntity> tableColumns = gentableColumnService.find(new GentableColumnQuery().tableId(table.getId()));
        table.setColumns(tableColumns);

        List<String> tableColumnNames = tableColumns.stream().map(GentableColumnEntity::getColumnName).collect(Collectors.toList());

        List<GentableColumnEntity> dbTableColumns = gentableColumnService.selectDbTableColumnsByName(tableName);
        if (StringUtils.isEmpty(dbTableColumns)) {
            throw new AppException("同步数据失败，原表结构不存在");
        }
        List<String> dbTableColumnNames = dbTableColumns.stream().map(GentableColumnEntity::getColumnName).collect(Collectors.toList());

        dbTableColumns.forEach(column -> {
            if (!tableColumnNames.contains(column.getColumnName())) {
                GenUtils.initColumnField(column, table);
                gentableColumnService.save(column);
            }
        });

        List<GentableColumnEntity> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
        if (StringUtils.isNotEmpty(delColumns)) {
            Long[] ids = delColumns.stream().map(GentableColumnEntity::getId).toArray(Long[]::new);
            gentableColumnService.remove(ids, null);
        }
    }

    @Override
    public void importDoc(MultipartFile file, Context context) {

        List<GentableEntity> tableList = ReadDoc.readWord(file);
        String operName = context.getUser().getLoginName();
        try {
            for (GentableEntity table : tableList) {
                GenUtils.initTable(table, operName);
                table.setGenType(0);
                table.setGenPath("/");
                table.setTplCategory("crud");
                table.setCacheServiceType(CacheServiceTypeEnum.NORMAL_SERVICE.getValue());

                //查询如果存在相同表名，先删除后再添加
                GentableEntity gentableEntity = this.selectOne(new GentableQuery().tableName(table.getTableName()));
                if (!ObjectUtils.isEmpty(gentableEntity)) {
                    //删除表
                    this.remove(gentableEntity.getId(), context);
                    gentableColumnService.remove(gentableEntity.getColumns().stream().map(item -> item.getId()).toArray(Long[]::new), context);

                }

                //保存表
                this.save(table);
                //保存列
                List<GentableColumnEntity> genTableColumns = table.getColumns();

                //校验是否有主键列，如果没有，默认第一列为主键
                GentableColumnEntity columnEntity = genTableColumns.stream().filter(f -> f.getIsPrimaryKey() != null).filter(f -> f.getIsPrimaryKey() == 1).findFirst().orElseGet(() -> null);
                if (ObjectUtils.isEmpty(columnEntity)) {
                    genTableColumns.get(0).setIsPrimaryKey(YesNoEnum.YES.getValue());
                }

                for (GentableColumnEntity column : genTableColumns) {
                    GenUtils.initColumnField(column, table);
                    gentableColumnService.save(column);
                }
            }
        } catch (Exception e) {
            throw new AppException("导入失败：" + e.getMessage());
        }

    }

    @Override
    public byte[] exportDoc(ProjectInfoEntity projectInfoEntity) {
        return generatorDoc(projectInfoEntity);
    }

    @Override
    public void crateTableBySql(String tableSql, Context context) {

        //校验部分命令 禁止使用 比如清库的

//        if(tableSql.indexOf("")!=-1){
//            throw new AppException("禁止使用");
//        }

        this.getDao().createTable(tableSql);
    }
    /**
     * 查询表信息并生成代码
     */
    private void generatorCode(String tableName, ZipOutputStream zip) {
        GentableEntity gentableEntity = this.selectOne(new GentableQuery().tableName(tableName));
        if (!ObjectUtils.isEmpty(gentableEntity)) {
            List<GentableColumnEntity> columnEntities = this.gentableColumnService.find(new GentableColumnQuery().tableId(gentableEntity.getId())).stream().map(column -> {
                String temp1 = StringUtils.substringBetween(column.getColumnComment(), "(", ")");
                String temp2 = StringUtils.substringBetween(column.getColumnComment(), "（", "）");
                GenUtils.checkDict(column, temp1);
                GenUtils.checkDict(column, temp2);
                return column;
            }).collect(Collectors.toList());

            gentableEntity.setColumns(columnEntities);

            // 设置主子表信息
            setSubTable(gentableEntity);
            // 设置主键列信息
            setPkColumn(gentableEntity);

            //生成模板基础数据
            Map data = FreeMarkerUtils.prepareSetFreeMarkerData(gentableEntity);

            List<String> templates = FreeMarkerUtils.getTemplateList(gentableEntity.getTplCategory());
            for (String temp : templates) {
                //通过模板生成不同的业务类
                try {
                    FreeMarkerUtils.initFreeMarker(genFreePath(temp));
                    StringWriter sw = new StringWriter();
                    Template template = FreeMarkerUtils.getTemplate(temp);
                    template.process(data, sw);
                    // 添加到zip
                    if (template.getName().equals("menu.sql.ftl")) {
                        FreeMarkerUtils.menuSql.append(sw.toString());
                    } else if (template.getName().equals("db.sql.ftl")) {
                        FreeMarkerUtils.dbSql.append(sw.toString());
                    } else {
                        zip.putNextEntry(new ZipEntry(FreeMarkerUtils.getFileName(template.getName(), gentableEntity)));
                        IOUtils.write(sw.toString(), zip, Constant.UTF8);
                    }

                    IOUtils.closeQuietly(sw);
                    zip.flush();
                    zip.closeEntry();
                } catch (Exception e) {
                    log.error("渲染模板失败，表名：" + gentableEntity.getTableName(), e);
                }
            }

        }
    }

    /**
     * 生成文档
     */
    private byte[] generatorDoc(ProjectInfoEntity projectInfo) {
        //构建列表
        String[] tableNames = Convert.toStrArray(projectInfo.getTables());
        List<GentableEntity> list = new ArrayList<>();

        for (String tableName : tableNames) {
            GentableEntity gentableEntity = this.selectOne(new GentableQuery().tableName(tableName));
            if (!ObjectUtils.isEmpty(gentableEntity)) {
                List<GentableColumnEntity> columnEntities = this.gentableColumnService.find(new GentableColumnQuery().tableId(gentableEntity.getId()));
                gentableEntity.setColumns(columnEntities);
                // 设置主子表信息
                setSubTable(gentableEntity);
                // 设置主键列信息
                setPkColumn(gentableEntity);
                list.add(gentableEntity);
            }
        }
        Map data = FreeMarkerUtils.prepareSetFreeMarkerDataByDoc(list, projectInfo);
        //通过模板生成不同的业务类
        String temp = "db.doc.ftl";
        try {
            FreeMarkerUtils.initFreeMarker(genFreePath(temp));
            StringWriter sw = new StringWriter();
            Template template = FreeMarkerUtils.getTemplate(temp);
            template.process(data, sw);
            return sw.toString().getBytes(Constant.UTF8);
        } catch (Exception e) {
            log.error("渲染模板失败：", e);
            return null;
        }

    }

    /**
     * 设置主键列信息
     *
     * @param table 业务表信息
     */
    public void setPkColumn(GentableEntity table) {
        for (GentableColumnEntity column : table.getColumns()) {
            if (column.getIsPrimaryKey() == YesNoEnum.YES.getValue()) {
                //是主键 设置主键列
                table.setPkColumn(column);
                break;
            }
        }
        if (StringUtils.isNull(table.getPkColumn())) {
            table.setPkColumn(table.getColumns().get(0));
        }
        if (GenConstants.TPL_SUB.equals(table.getTplCategory()) && !ObjectUtils.isEmpty(table.getSubTable())) {
            for (GentableColumnEntity column : table.getSubTable().getColumns()) {
                if (column.getIsPrimaryKey() == YesNoEnum.YES.getValue()) {
                    table.getSubTable().setPkColumn(column);
                    break;
                }
            }
            if (StringUtils.isNull(table.getSubTable().getPkColumn())) {
                table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));
            }
        }
    }

    /**
     * 设置主子表信息
     *
     * @param table 业务表信息
     */
    public void setSubTable(GentableEntity table) {
        String subTableName = table.getSubTableName();
        if (StringUtils.isNotEmpty(subTableName)) {
            GentableEntity gentableEntity = this.selectOne(new GentableQuery().tableName(subTableName));
            if (!ObjectUtils.isEmpty(gentableEntity)) {
                List<GentableColumnEntity> columnEntities = gentableColumnService.find(new GentableColumnQuery().tableId(gentableEntity.getId()));
                gentableEntity.setColumns(columnEntities);
                setPkColumn(gentableEntity);
                table.setSubTable(gentableEntity);
            }
        }
    }


    @Override
    protected void removeAfter(Long[] ids, Context context, int result) throws AppException {
        Long[] colIds = gentableColumnService.find(new GentableColumnQuery().tableIdList(Arrays.asList(ids))).stream().map(GentableColumnEntity::getId).toArray(Long[]::new);
        gentableColumnService.remove(colIds, context);
        super.removeAfter(ids, context, result);
    }

    private String genFreePath(String temp) {
        String path = "";
        if (temp.indexOf(".java.") != -1) {
            path = "java";
        } else if (temp.indexOf(".sql.") != -1) {
            path = "sql";
        } else if (temp.indexOf(".xml.") != -1) {
            path = "xml";
        } else if (temp.indexOf(".vue.") != -1) {
            path = "vue";
        } else if (temp.indexOf(".doc.") != -1) {
            path = "doc";
        }
        return path;
    }
}