package com.ruoyi.generator.service.front;

import com.ruoyi.generator.domain.GenProject;
import com.ruoyi.generator.domain.front.FrontProject;
import com.ruoyi.generator.domain.front.FrontProjectTable;
import com.ruoyi.generator.domain.front.dto.PreviewFileRequest;
import com.ruoyi.generator.mapper.front.FrontProjectColumnMapper;
import com.ruoyi.generator.mapper.front.FrontProjectMapper;
import com.ruoyi.generator.mapper.front.FrontProjectTableMapper;
import com.ruoyi.generator.service.IGenProjectService;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

@Service
public class FrontProjectPreviewServiceImpl implements IFrontProjectPreviewService
{
    @Autowired
    private IFrontProjectService frontProjectService;
    @Autowired
    private IGenProjectService genProjectService;
    @Autowired
    private FrontProjectTableMapper frontProjectTableMapper;
    @Autowired
    private FrontProjectColumnMapper frontProjectColumnMapper;
    @Autowired
    private FrontProjectMapper frontProjectMapper;

    @Override
    public List<Map<String, Object>> getStructure(Long userId, Long projectId, String templateType)
    {
        GenProject genProject = loadGenProject(userId, projectId);
        assertTemplateTypeEnabled(genProject, templateType);
        return genProjectService.getProjectStructure(genProject, templateType);
    }

    @Override
    public Map<String, String> getFileContent(Long userId, Long projectId, PreviewFileRequest request)
    {
        if (request == null)
        {
            throw new ServiceException("预览文件参数不能为空");
        }
        GenProject genProject = loadGenProject(userId, projectId);
        assertTemplateTypeEnabled(genProject, request.getTemplateType());
        return genProjectService.previewCode(genProject, request.getTableId(), request.getCategory(), request.getTemplateType());
    }

    @Override
    public byte[] download(Long userId, Long projectId, String templateType)
    {
        GenProject genProject = loadGenProject(userId, projectId);
        assertTemplateTypeEnabled(genProject, templateType);
        return genProjectService.downloadStructure(genProject, templateType);
    }

    @Override
    public byte[] downloadAll(Long userId, Long projectId)
    {
        GenProject genProject = loadGenProject(userId, projectId);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (ZipOutputStream zip = new ZipOutputStream(outputStream))
        {
            appendZip(zip, "backend", genProjectService.downloadStructure(genProject, "backend"));
            if (isFrontendEnabled(genProject))
            {
                appendZip(zip, "front-web", genProjectService.downloadStructure(genProject, "frontend"));
            }
            appendZip(zip, "admin-web", genProjectService.downloadStructure(genProject, "admin_frontend"));
        }
        catch (IOException e)
        {
            throw new ServiceException("生成项目源码压缩包失败");
        }
        return outputStream.toByteArray();
    }

    @Override
    public int markPreviewReady(Long userId, Long projectId)
    {
        frontProjectService.getProject(userId, projectId);
        FrontProject project = new FrontProject();
        project.setProjectId(projectId);
        project.setPreviewStatus("1");
        return frontProjectMapper.updateFrontProject(project);
    }

    private GenProject loadGenProject(Long userId, Long projectId)
    {
        FrontProject frontProject = frontProjectService.getProject(userId, projectId);
        if (frontProject.getTables() == null)
        {
            List<FrontProjectTable> tables = frontProjectTableMapper.selectTablesByProjectId(projectId);
            if (tables == null)
            {
                tables = new ArrayList<FrontProjectTable>();
            }
            for (FrontProjectTable table : tables)
            {
                table.setColumns(frontProjectColumnMapper.selectColumnsByTableId(table.getTableId()));
            }
            frontProject.setTables(tables);
        }
        return FrontProjectConverter.toGenProject(frontProject);
    }

    private void assertTemplateTypeEnabled(GenProject project, String templateType)
    {
        if ("frontend".equals(templateType) && !isFrontendEnabled(project))
        {
            throw new ServiceException("该项目未启用前台前端");
        }
    }

    private boolean isFrontendEnabled(GenProject project)
    {
        return project == null || !"0".equals(project.getFrontendEnabled());
    }

    private void appendZip(ZipOutputStream targetZip, String rootFolder, byte[] sourceData) throws IOException
    {
        ZipInputStream sourceZip = new ZipInputStream(new ByteArrayInputStream(sourceData));
        ZipEntry sourceEntry;
        byte[] buffer = new byte[4096];
        while ((sourceEntry = sourceZip.getNextEntry()) != null)
        {
            String entryName = normalizeEntryName(rootFolder, sourceEntry.getName());
            if (entryName == null)
            {
                sourceZip.closeEntry();
                continue;
            }
            if (sourceEntry.isDirectory())
            {
                if (!entryName.endsWith("/"))
                {
                    entryName = entryName + "/";
                }
                targetZip.putNextEntry(new ZipEntry(entryName));
                targetZip.closeEntry();
            }
            else
            {
                targetZip.putNextEntry(new ZipEntry(entryName));
                int length;
                while ((length = sourceZip.read(buffer)) > -1)
                {
                    targetZip.write(buffer, 0, length);
                }
                targetZip.closeEntry();
            }
            sourceZip.closeEntry();
        }
        sourceZip.close();
    }

    private String normalizeEntryName(String rootFolder, String entryName)
    {
        if (entryName == null || entryName.trim().length() == 0)
        {
            return null;
        }
        String normalized = entryName.replace('\\', '/');
        while (normalized.startsWith("/"))
        {
            normalized = normalized.substring(1);
        }
        if (normalized.startsWith("../") || normalized.contains("/../"))
        {
            throw new ServiceException("项目源码压缩包路径不合法");
        }
        return rootFolder + "/" + normalized;
    }
}
