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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiaoba.common.base.constant.BaseConstant;
import com.qiaoba.common.base.enums.BaseEnum;
import com.qiaoba.common.base.exception.ServiceException;
import com.qiaoba.common.database.context.TenantDbTypeContext;
import com.qiaoba.common.database.entity.PageQuery;
import com.qiaoba.common.database.entity.TableDataInfo;
import com.qiaoba.module.generator.entity.Generator;
import com.qiaoba.module.generator.entity.GeneratorTable;
import com.qiaoba.module.generator.entity.GeneratorTableColumn;
import com.qiaoba.module.generator.entity.dto.TableDto;
import com.qiaoba.module.generator.entity.vo.DbTableVo;
import com.qiaoba.module.generator.entity.vo.TableColumnVo;
import com.qiaoba.module.generator.factory.VelocityFactory;
import com.qiaoba.module.generator.mapper.GeneratorTableMapper;
import com.qiaoba.module.generator.service.GeneratorTableColumnService;
import com.qiaoba.module.generator.service.GeneratorTableService;
import com.qiaoba.module.generator.util.GeneratorUtil;
import com.qiaoba.module.generator.util.VelocityUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 数据库表 服务层实现
 *
 * @author ailanyin
 * @version 1.0
 * @since 2023/6/26 10:12
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class GeneratorTableServiceImpl implements GeneratorTableService {

    private final GeneratorTableMapper generatorTableMapper;
    private final GeneratorTableColumnService generatorTableColumnService;

    @Override
    public TableDataInfo<GeneratorTable> selectPageList(TableDto dto, PageQuery pageQuery) {
        QueryWrapper<GeneratorTable> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .like(StrUtil.isNotBlank(dto.getTableName()), GeneratorTable::getTableName, dto.getTableName())
                .like(StrUtil.isNotBlank(dto.getTableComment()), GeneratorTable::getTableComment, dto.getTableComment());
        return TableDataInfo.build(generatorTableMapper.selectPage(pageQuery.build(), wrapper));
    }

    @Override
    public GeneratorTable selectById(String tableId) {
        return generatorTableMapper.selectById(tableId);
    }

    @Override
    public int updateById(GeneratorTable generatorTable) {
        return generatorTableMapper.updateById(generatorTable);
    }

    @Override
    public TableDataInfo<DbTableVo> selectPageDbTableList(TableDto dto, PageQuery pageQuery) {
        dto.setDbType(TenantDbTypeContext.getDefault());
        dto.setSchema(selectSchema());
        return TableDataInfo.build(generatorTableMapper.selectPageDbTableList(pageQuery.build(), dto));
    }

    @Override
    public GeneratorTable selectByTableId(String tableId, boolean allowNull) {
        GeneratorTable generatorTable = generatorTableMapper.selectById(tableId);
        if (Objects.isNull(generatorTable) && !allowNull) {
            throw new ServiceException(StrUtil.format("未查到表信息, 表ID: {}", tableId));
        }
        return generatorTable;
    }

    @Override
    public int importTable(TableDto dto) {
        GeneratorTable generatorTable = GeneratorUtil.dtoToGeneratorTable(dto);
        int result = generatorTableMapper.insert(generatorTable);
        if (result > BaseConstant.HANDLE_ERROR) {
            List<TableColumnVo> columnVos = generatorTableColumnService.selectDbColumnList(dto.getTableName(), false);
            for (TableColumnVo columnVo : columnVos) {
                GeneratorTableColumn generatorTableColumn = GeneratorUtil.initColumn(columnVo, generatorTable.getTableId());
                generatorTableColumnService.insert(generatorTableColumn);
            }
        }
        return result;
    }

    @Override
    public byte[] downloadCode(String tableId, String templateType) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        generatorCode(tableId, templateType, zip);
        IoUtil.close(zip);
        return outputStream.toByteArray();
    }

    @Override
    public Map<String, String> previewCode(String tableId, String templateType) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        // 查询表信息
        GeneratorTable table = selectByTableId(tableId, false);
        // 查询字段信息
        List<GeneratorTableColumn> columns = generatorTableColumnService.selectListByTableId(table.getTableId(), false);
        Generator generator = new Generator(table, columns);
        // 设置主键列信息
        setPkColumn(generator);
        // 设置查询字段信息
        setQueryColumns(generator);
        // 初始化 Velocity
        VelocityFactory.init();
        // 设置参数
        VelocityContext context = VelocityUtil.setVariables(generator);
        // 获取模板列表
        List<String> templates = VelocityUtil.getTemplateList(templateType);
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, BaseConstant.UTF8);
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return dataMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByIds(List<String> tableIds) {
        int result = generatorTableMapper.deleteBatchIds(tableIds);
        if (result > BaseConstant.HANDLE_ERROR) {
            generatorTableColumnService.deleteByTableIds(tableIds);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncTable(String tableId) {
        // 查询表信息
        GeneratorTable table = selectByTableId(tableId, false);
        // 查询最新表结构
        List<TableColumnVo> dbColumns = generatorTableColumnService.selectDbColumnList(table.getTableName(), false);
        // 查询保存的表结构
        List<GeneratorTableColumn> generatorTableColumns = generatorTableColumnService.selectListByTableId(tableId, false);
        // 构造比较map
        Map<String, GeneratorTableColumn> generatorTableColumnMap = generatorTableColumns.stream().collect(Collectors.toMap(GeneratorTableColumn::getColumnName, Function.identity()));
        List<String> dbTableColumnNames = dbColumns.stream().map(TableColumnVo::getColumnName).collect(Collectors.toList());
        // 开始比较
        dbColumns.forEach(column -> {
            GeneratorTableColumn generatorTableColumn = GeneratorUtil.initColumn(column, tableId);
            // 字段名不变
            if (generatorTableColumnMap.containsKey(column.getColumnName())) {
                GeneratorTableColumn prevColumn = generatorTableColumnMap.get(column.getColumnName());
                generatorTableColumn.setColumnId(prevColumn.getColumnId());
                if (BaseEnum.YES.getCode().equals(generatorTableColumn.getIsList())) {
                    // 如果是列表，继续保留查询方式/字典类型选项
                    generatorTableColumn.setDictType(prevColumn.getDictType());
                    generatorTableColumn.setQueryType(prevColumn.getQueryType());
                }
                generatorTableColumnService.updateById(generatorTableColumn);
            } else {
                generatorTableColumnService.insert(generatorTableColumn);
            }
        });

        List<String> deleteIds = new ArrayList<>();
        for (GeneratorTableColumn generatorTableColumn : generatorTableColumns) {
            if (!dbTableColumnNames.contains(generatorTableColumn.getColumnName())) {
                deleteIds.add(generatorTableColumn.getColumnId());
            }
        }
        if (CollUtil.isNotEmpty(deleteIds)) {
            generatorTableColumnService.deleteByIds(deleteIds);
        }
    }

    private void generatorCode(String tableId, String templateType, ZipOutputStream zip) {
        // 查询表信息
        GeneratorTable table = selectByTableId(tableId, false);
        Map<String, String> dataMap = previewCode(tableId, templateType);
        for (String key : dataMap.keySet()) {
            String code = dataMap.get(key);
            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(VelocityUtil.getFileName(key, table)));
                IoUtil.write(zip, false, code.getBytes());
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + table.getTableName(), e);
            }
        }
    }

    private String selectSchema() {
        // PgSQL 需要设置 schema
        //DataBaseEnum.POSTGRE_SQL.getType().equals(dbType)
        return null;
    }

    /**
     * 设置主键列信息
     *
     * @param generator generator
     */
    private void setPkColumn(Generator generator) {
        for (GeneratorTableColumn column : generator.getColumns()) {
            if (BaseEnum.YES.getCode().equals(column.getIsPk())) {
                generator.setPkColumn(column);
                break;
            }
        }
        if (ObjectUtil.isNull(generator.getPkColumn())) {
            generator.setPkColumn(generator.getColumns().get(0));
        }
    }

    private void setQueryColumns(Generator generator) {
        List<GeneratorTableColumn> queryColumns = new ArrayList<>();
        for (GeneratorTableColumn column : generator.getColumns()) {
            if (generator.isQueryColumn(column.getIsQuery())) {
                queryColumns.add(column);
            }
        }
        generator.setQueryColumns(queryColumns);
    }
}
