package com.ruoyi.generator.service;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.*;
import com.ruoyi.generator.mapper.GenProjectMapper;
import com.ruoyi.generator.mapper.GenTableMapper;
import com.ruoyi.generator.util.VelocityInitializer;
import com.ruoyi.generator.util.VelocityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

/**
 * 项目表 服务层实现
 *
 * @author ruoyi
 */
@Service
public class GenProjectServiceImpl implements IGenProjectService {
    @Autowired
    private GenProjectMapper genProjectMapper;
    @Autowired
    private GenTableMapper genTableMapper;
    @Autowired
    private ITemplateService templateService;
    @Autowired
    private ITemplateFileService templateFileService;
    @Autowired
    private ISysCodeSnippetService codeSnippetService;
    @Autowired
    private ISysProjectModuleService sysProjectModuleService;
    @Autowired
    private ISysProjectTemplateService sysProjectTemplateService;
    @Autowired
    private ISysProjectStructureService sysProjectStructureService;

    /**
     * 查询项目信息
     *
     * @param projectId 项目ID
     * @return 项目信息
     */
    @Override
    public GenProject selectGenProjectById(Long projectId) {
        return genProjectMapper.selectGenProjectById(projectId);
    }

    /**
     * 查询项目列表
     *
     * @param genProject 项目信息
     * @return 项目集合
     */
    @Override
    public List<GenProject> selectGenProjectList(GenProject genProject) {
        return genProjectMapper.selectGenProjectList(genProject);
    }

    /**
     * 新增项目
     *
     * @param genProject 项目信息
     * @return 结果
     */
    @Override
    public int insertGenProject(GenProject genProject) {
        return genProjectMapper.insertGenProject(genProject);
    }

    /**
     * 修改项目
     *
     * @param genProject 项目信息
     * @return 结果
     */
    @Override
    public int updateGenProject(GenProject genProject) {
        return genProjectMapper.updateGenProject(genProject);
    }

    /**
     * 删除项目对象
     *
     * @param projectId 项目ID
     * @return 结果
     */
    @Override
    public int deleteGenProjectById(Long projectId) {
        return genProjectMapper.deleteGenProjectById(projectId);
    }

    /**
     * 批量删除项目信息
     *
     * @param projectIds 需要删除的项目ID
     * @return 结果
     */
    @Override
    public int deleteGenProjectByIds(Long[] projectIds) {
        return genProjectMapper.deleteGenProjectByIds(projectIds);
    }

    /**
     * 获取项目结构
     *
     * @param projectId 项目ID
     * @return 项目结构
     */
    @Override
    public List<Map<String, Object>> getProjectStructure(Long projectId, String type) {
        GenProject project = getRequiredProject(projectId);
       
        List<GenTable> projectTables = genTableMapper.selectGenTablesByProjectId(projectId);
        project.setTables(projectTables);

        List<Map<String, Object>> structure = new ArrayList<>();

        // 获取项目模板
        SysProjectTemplate projectTemplate = getRequiredProjectTemplate(project, type);

        // 获取模板关联的项目结构节点
        List<SysProjectStructure> structureNodes = sysProjectStructureService.selectProjectStructureListByModule(project.getProjectId(), projectTemplate.getTemplateId());

        // 构建树形结构
        Map<Long, SysProjectStructure> sourceNodeMap = new HashMap<>();
        Map<String, Map<String, Object>> nodeMap = new LinkedHashMap<>();
        for (SysProjectStructure node : structureNodes) {
            sourceNodeMap.put(node.getNodeId(), node);
            if (isTableScopedNode(node)) {
                for (GenTable table : projectTables) {
                    Map<String, Object> tableNodeData = createStructureNodeData(node, project, table);
                    nodeMap.put(getNodeKey(node.getNodeId(), table.getTableId()), tableNodeData);
                }
            } else {
                Map<String, Object> nodeData = createStructureNodeData(node, project, null);
                nodeMap.put(getNodeKey(node.getNodeId(), null), nodeData);
            }
        }

        // 构建父子关系
        for (SysProjectStructure node : structureNodes) {
            if (isTableScopedNode(node)) {
                // 处理表特定的节点
                for (GenTable table : projectTables) {
                    Map<String, Object> nodeData = nodeMap.get(getNodeKey(node.getNodeId(), table.getTableId()));
                    addNodeToParent(structure, nodeData, node, nodeMap, sourceNodeMap, table);
                }
            } else {
                // 处理普通节点
                Map<String, Object> nodeData = nodeMap.get(getNodeKey(node.getNodeId(), null));
                addNodeToParent(structure, nodeData, node, nodeMap, sourceNodeMap, null);
            }
        }

        return structure;
    }

    @Override
    public byte[] downloadStructure(Long projectId, String type) {
        GenProject project = getRequiredProject(projectId);

        List<GenTable> projectTables = genTableMapper.selectGenTablesByProjectId(projectId);
        project.setTables(projectTables);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        try {
            // 获取项目结构
            List<Map<String, Object>> structure = getProjectStructure(projectId, type);

            // 递归处理项目结构
            processStructure(structure, zip, project, type);

            zip.close();
            return outputStream.toByteArray();
        } catch (IOException e) {
            throw new ServiceException("生成项目结构失败");
        }
    }

    private void processStructure(List<Map<String, Object>> structure, ZipOutputStream zip, GenProject project, String type) throws IOException {
        processStructureWithPath(structure, zip, project, "", type);
    }

    private void processStructureWithPath(List<Map<String, Object>> structure, ZipOutputStream zip, GenProject project, String currentPath, String type) throws IOException {
        for (Map<String, Object> node : structure) {
            String nodeType = (String) node.get("type");
            String name = (String) node.get("name");
            String category = (String) node.get("category");
            Object tableId = node.get("tableId");
            String fullPath = currentPath + name;

            if ("folder".equals(nodeType)) {
                // 创建目录，确保以/结尾
                String folderPath = fullPath + "/";
                zip.putNextEntry(new ZipEntry(folderPath));
                zip.closeEntry();

                // 处理子节点，传递当前完整路径
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> children = (List<Map<String, Object>>) node.get("children");
                if (children != null && !children.isEmpty()) {
                    processStructureWithPath(children, zip, project, folderPath, type);
                }
            } else if ("file".equals(nodeType)) {
                // 生成文件内容
                String content = generateFileContent(project, category, (Long) tableId, type);
                if (content != null) {
                    zip.putNextEntry(new ZipEntry(fullPath));
                    zip.write(content.getBytes(StandardCharsets.UTF_8));
                    zip.closeEntry();
                }
            }
        }
    }

    /**
     * 预览代码
     *
     * @param tableId 表编号
     * @return 预览数据列表
     */
    @Override
    public Map<String, String> previewCode(Long projectId, Long tableId, String keyword, String type)
    {
        Map<String, String> dataMap = new LinkedHashMap<>();
        GenProject project = getRequiredProject(projectId);
        List<GenTable> projectTables = genTableMapper.selectGenTablesByProjectId(projectId);
        project.setTables(projectTables);

        SysProjectModule projectModuleFilter = new SysProjectModule();
        projectModuleFilter.setProjectId(project.getProjectId());
        List<SysProjectModule> modules = sysProjectModuleService.selectSysProjectModuleList(projectModuleFilter);

        VelocityInitializer.initVelocity();

        // 获取模板列表
        SysProjectTemplate projectTemplate = getRequiredProjectTemplate(project, type);
        List<TemplateFile> templateFileList = getRequiredTemplateFiles(projectTemplate.getTemplateId());
        for (TemplateFile templateFile : templateFileList)
        {
            if(matchesTemplateFile(templateFile, keyword)){
                // 渲染模板
                dataMap.put(templateFile.getFileName(), renderTemplate(project, tableId, modules, templateFile));
            }
        }
        return dataMap;
    }

    private String generateFileContent(GenProject project, String category, Long tableId, String type) {
        if (project == null) {
            throw new ServiceException("项目信息不能为空");
        }
        SysProjectModule projectModuleFilter = new SysProjectModule();
        projectModuleFilter.setProjectId(project.getProjectId());
        List<SysProjectModule> modules = sysProjectModuleService.selectSysProjectModuleList(projectModuleFilter);

        VelocityInitializer.initVelocity();

        // 获取模板列表
        SysProjectTemplate projectTemplate = getRequiredProjectTemplate(project, type);
        List<TemplateFile> templateFileList = getRequiredTemplateFiles(projectTemplate.getTemplateId());
        for (TemplateFile templateFile : templateFileList)
        {
            if(matchesTemplateFile(templateFile, category)){
                try {
                    return renderTemplate(project, tableId, modules, templateFile);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ServiceException("渲染模板失败，表名：" + tableId);
                }
            }
        }
        throw new ServiceException("未找到模板文件：" + category);
    }

    private GenProject getRequiredProject(Long projectId) {
        GenProject project = genProjectMapper.selectGenProjectById(projectId);
        if (project == null) {
            throw new ServiceException("项目不存在");
        }
        return project;
    }

    private SysProjectTemplate getRequiredProjectTemplate(GenProject project, String type) {
        SysProjectTemplate projectTemplate = sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(project.getProjectId(), type);
        if (projectTemplate == null || projectTemplate.getTemplateId() == null) {
            throw new ServiceException("项目未配置" + type + "模板");
        }
        return projectTemplate;
    }

    private List<TemplateFile> getRequiredTemplateFiles(Long templateId) {
        List<TemplateFile> templateFileList = templateFileService.selectTemplateFilesByTemplateId(templateId);
        if (StringUtils.isEmpty(templateFileList)) {
            throw new ServiceException("模板未配置文件");
        }
        return templateFileList;
    }

    private boolean isTableScopedNode(SysProjectStructure node) {
        return node.getTableId() != null && node.getTableId() == 0;
    }

    private String getNodeKey(Long nodeId, Long tableId) {
        return tableId == null ? String.valueOf(nodeId) : nodeId + "-" + tableId;
    }

    private Map<String, Object> createStructureNodeData(SysProjectStructure node, GenProject project, GenTable table) {
        Map<String, Object> nodeData = new LinkedHashMap<>();
        nodeData.put("id", table == null ? node.getNodeId() : getNodeKey(node.getNodeId(), table.getTableId()));
        nodeData.put("name", replaceNodeName(node.getNodeName(), project, table));
        nodeData.put("type", node.getNodeType());
        nodeData.put("category", node.getCategory());
        nodeData.put("tableId", table == null ? node.getTableId() : table.getTableId());
        if ("folder".equals(node.getNodeType())) {
            nodeData.put("children", new ArrayList<>());
        }
        return nodeData;
    }

    private String replaceNodeName(String nodeName, GenProject project, GenTable table) {
        if (nodeName == null) {
            return "";
        }
        String result = nodeName;
        result = result.replace("{projectName}", StringUtils.defaultString(project.getProjectFileName()));
        result = result.replace("{packageName}", StringUtils.defaultString(project.getPackageName()));
        if (table != null) {
            result = result.replace("{ClassName}", StringUtils.defaultString(table.getClassName()));
            result = result.replace("{className}", StringUtils.uncapitalize(StringUtils.defaultString(table.getClassName())));
            result = result.replace("{tableName}", StringUtils.defaultString(table.getTableName()));
            result = result.replace("{businessName}", StringUtils.defaultString(table.getBusinessName()));
        }
        return result;
    }

    private void addNodeToParent(List<Map<String, Object>> roots, Map<String, Object> nodeData,
                                 SysProjectStructure node, Map<String, Map<String, Object>> nodeMap,
                                 Map<Long, SysProjectStructure> sourceNodeMap, GenTable table) {
        if (nodeData == null) {
            return;
        }
        if (node.getParentId() == null || node.getParentId() == 0) {
            roots.add(nodeData);
            return;
        }

        SysProjectStructure parentSource = sourceNodeMap.get(node.getParentId());
        Long parentTableId = parentSource != null && isTableScopedNode(parentSource) && table != null ? table.getTableId() : null;
        Map<String, Object> parentNode = nodeMap.get(getNodeKey(node.getParentId(), parentTableId));
        if (parentNode == null) {
            return;
        }

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> children = (List<Map<String, Object>>) parentNode.get("children");
        if (children != null) {
            children.add(nodeData);
        }
    }

    private boolean matchesTemplateFile(TemplateFile templateFile, String templateFileName) {
        if (templateFile == null || StringUtils.isEmpty(templateFileName)) {
            return false;
        }
        return StringUtils.equals(templateFile.getFileName(), templateFileName)
                || StringUtils.equals(templateFile.getFilePath(), templateFileName);
    }

    private String renderTemplate(GenProject project, Long tableId, List<SysProjectModule> modules, TemplateFile templateFile) {
        VelocityContext context = prepareVelocityContext(project, tableId, modules, templateFile);
        StringWriter sw = new StringWriter();
        Velocity.evaluate(context, sw, "", StringUtils.defaultString(templateFile.getFileContent()));
        return sw.toString();
    }

    private VelocityContext prepareVelocityContext(GenProject project, Long tableId, List<SysProjectModule> modules, TemplateFile templateFile) {
        if (tableId != null && tableId != -1) {
            GenTable table = getRequiredTable(tableId);
            table.setModules(modules);
            table.setPackageName(project.getPackageName());
            // 设置主子表信息
            setSubTable(table);
            // 设置主键列信息
            setPkColumn(table);
            VelocityContext context = VelocityUtils.prepareContext(table);
            putSnippetVariables(context, table, modules, templateFile);
            return context;
        }
        return VelocityUtils.prepareContextProject(project);
    }

    private GenTable getRequiredTable(Long tableId) {
        GenTable table = genTableMapper.selectGenTableById(tableId);
        if (table == null) {
            throw new ServiceException("业务表不存在：" + tableId);
        }
        if (StringUtils.isEmpty(table.getColumns())) {
            throw new ServiceException("业务表未配置字段：" + table.getTableName());
        }
        return table;
    }

    private void putSnippetVariables(VelocityContext context, GenTable table, List<SysProjectModule> modules, TemplateFile templateFile) {
        SysCodeSnippet codeSnippet = new SysCodeSnippet();
        codeSnippet.setStatus("0");
        List<SysCodeSnippet> codeSnippetList = codeSnippetService.selectSysCodeSnippetList(codeSnippet);
        if (StringUtils.isEmpty(codeSnippetList)) {
            return;
        }

        Map<String, StringBuilder> snippetContentMap = new LinkedHashMap<>();
        for (SysCodeSnippet snippet : codeSnippetList) {
            if (StringUtils.isNotEmpty(snippet.getInsertPoint())) {
                snippetContentMap.putIfAbsent(snippet.getInsertPoint(), new StringBuilder());
            }
        }
        for (SysCodeSnippet snippet : codeSnippetList) {
            if (matchesSnippet(snippet, table, modules, templateFile)) {
                StringBuilder content = snippetContentMap.get(snippet.getInsertPoint());
                if (content.length() > 0) {
                    content.append(System.lineSeparator());
                }
                content.append(snippet.getSnippetContent());
            }
        }
        for (Map.Entry<String, StringBuilder> entry : snippetContentMap.entrySet()) {
            context.put(entry.getKey(), entry.getValue().toString());
        }
    }

    private boolean matchesSnippet(SysCodeSnippet snippet, GenTable table, List<SysProjectModule> modules, TemplateFile templateFile) {
        return snippet != null
                && StringUtils.isNotEmpty(snippet.getInsertPoint())
                && StringUtils.isNotEmpty(snippet.getSnippetContent())
                && isSnippetEnabled(snippet)
                && matchesSnippetModule(snippet, modules)
                && matchesSnippetTable(snippet, table)
                && matchesSnippetTemplate(snippet, templateFile);
    }

    private boolean isSnippetEnabled(SysCodeSnippet snippet) {
        return (StringUtils.isEmpty(snippet.getStatus()) || "0".equals(snippet.getStatus()))
                && (StringUtils.isEmpty(snippet.getDelFlag()) || "0".equals(snippet.getDelFlag()));
    }

    private boolean matchesSnippetModule(SysCodeSnippet snippet, List<SysProjectModule> modules) {
        if (snippet.getModuleId() == null) {
            return true;
        }
        if (StringUtils.isEmpty(modules)) {
            return false;
        }
        for (SysProjectModule module : modules) {
            if (snippet.getModuleId().equals(module.getModuleId())
                    && (StringUtils.isEmpty(module.getStatus()) || "0".equals(module.getStatus()))) {
                return true;
            }
        }
        return false;
    }

    private boolean matchesSnippetTable(SysCodeSnippet snippet, GenTable table) {
        return StringUtils.isEmpty(snippet.getRelationTableName())
                || StringUtils.equals(snippet.getRelationTableName(), table.getTableName());
    }

    private boolean matchesSnippetTemplate(SysCodeSnippet snippet, TemplateFile templateFile) {
        return StringUtils.isEmpty(snippet.getTemplateFileName())
                || matchesTemplateFile(templateFile, snippet.getTemplateFileName());
    }

    /**
     * 设置主键列信息
     *
     * @param table 业务表信息
     */
    private void setPkColumn(GenTable table) {
        for (GenTableColumn column : table.getColumns()) {
            if (column.isPk()) {
                table.setPkColumn(column);
                break;
            }
        }
        if (StringUtils.isNull(table.getPkColumn())) {
            table.setPkColumn(table.getColumns().get(0));
        }
    }

    /**
     * 设置主子表信息
     *
     * @param table 业务表信息
     */
    public void setSubTable(GenTable table) {
        String subTableName = table.getSubTableName();
        if (StringUtils.isNotEmpty(subTableName)) {
            table.setSubTable(genTableMapper.selectGenTableByName(subTableName));
        }
    }
}
