package com.ruoyi.system.service.impl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.system.entity.BpmProcess;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.BpmProcessMapper;
import com.ruoyi.system.service.IBpmProcessService;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * 流程定义Service业务层处理
 *
 * @author hzh
 * @date 2023-10-10
 */
@Service
public class BpmProcessServiceImpl implements IBpmProcessService {

    private static final Logger log = LoggerFactory.getLogger(BpmProcessServiceImpl.class);

    @Autowired
    private BpmProcessMapper bpmProcessMapper;

    /**
     * 查询流程定义
     *
     * @param processId 流程定义主键
     * @return 流程定义
     */
    @Override
    public BpmProcess selectBpmProcessByProcessId(Long processId) {
        return bpmProcessMapper.selectBpmProcessByProcessId(processId);
    }

    /**
     * 查询流程定义列表
     *
     * @param bpmProcess 流程定义
     * @return 流程定义
     */
    @Override
    public List<BpmProcess> selectBpmProcessList(BpmProcess bpmProcess) {
        return bpmProcessMapper.selectBpmProcessList(bpmProcess);
    }

    /**
     * 新增流程定义
     *
     * @param bpmProcess 流程定义
     * @return 结果
     */
    @Override
    public int insertBpmProcess(BpmProcess bpmProcess, MultipartFile fileXML) {
        String filePath = "";  // 文件存放路径
        if (!fileXML.isEmpty()) {
            try {
                // 解析得到当前文件中的内容
                byte[] bytes = fileXML.getBytes();
                String xmlContent = new String(bytes);
                // 存储标签内容
                bpmProcess.setProcessXmlContent(xmlContent);
                // 指定文件路径上传
                filePath = FileUploadUtils.upload(RuoYiConfig.getUploadXMLPath(), fileXML);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        bpmProcess.setProcessXmlPath(filePath);
        bpmProcess.setCreateTime(DateUtils.getNowDate());
        bpmProcess.setCreateBy(SecurityUtils.getUsername());
        bpmProcess.setProcessVersion(0L);
        return bpmProcessMapper.insertBpmProcess(bpmProcess);
    }

    /**
     * 修改流程定义
     *
     * @param bpmProcess 流程定义
     * @return 结果
     */
    @Override
    public int updateBpmProcess(BpmProcess bpmProcess, MultipartFile fileXML) {
        String filePath = "";  // 新的文件存放路径
        if (!fileXML.isEmpty()) {
            // 修改流程删除流程文件后重新生成
            FileUtils.deleteFile(RuoYiConfig.getProfile() + StringUtils.substringAfter(bpmProcess.getProcessXmlPath(), Constants.RESOURCE_PREFIX));
            try {
                // 解析得到当前文件中的内容
                byte[] bytes = fileXML.getBytes();
                String xmlContent = new String(bytes);
                // 存储标签内容
                bpmProcess.setProcessXmlContent(xmlContent);
                // 指定文件路径上传
                filePath = FileUploadUtils.upload(RuoYiConfig.getUploadXMLPath(), fileXML);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        bpmProcess.setProcessXmlPath(filePath);
        bpmProcess.setUpdateTime(DateUtils.getNowDate());
        bpmProcess.setUpdateBy(SecurityUtils.getUsername());
        return bpmProcessMapper.updateBpmProcess(bpmProcess);
    }

    /**
     * 批量删除流程定义
     *
     * @param processIds 需要删除的流程定义主键
     * @return 结果
     */
    @Override
    public int deleteBpmProcessByProcessIds(Long[] processIds) {
        return bpmProcessMapper.deleteBpmProcessByProcessIds(processIds);
    }

    /**
     * 删除流程定义信息
     *
     * @param processId 流程定义主键
     * @return 结果
     */
    @Override
    public int deleteBpmProcessByProcessId(Long processId) {
        return bpmProcessMapper.deleteBpmProcessByProcessId(processId);
    }

    /**
     * 备份
     * 需要实现历史版本功能 每次进行修改时就是一次版本的更新
     * 备份可以导出xml文件理论上实现复制流程
     *
     * @param bpmProcess
     * @return
     */
    @Override
    public int bpmBackups(BpmProcess bpmProcess) {
        bpmProcess = bpmProcessMapper.selectBpmProcessByProcessId(bpmProcess.getProcessId());
        bpmProcess.setCreateTime(DateUtils.getNowDate());
        bpmProcess.setCreateBy(SecurityUtils.getUsername());
        bpmProcess.setProcessVersion(1L);  // 设置为历史版本，备份状态
        String filePath = RuoYiConfig.getProfile() + StringUtils.substringAfter(bpmProcess.getProcessXmlPath(), Constants.RESOURCE_PREFIX);
        // 复制流程文件
        Path fileInfo = Paths.get(filePath);
        // 定义目标文件路径，注意这里我们只是改变了文件的名字，路径还是原来的路径
        Path destinationFile = Paths.get(filePath).resolveSibling("备份：" + fileInfo.getFileName().toString());
        try {
            Files.copy(fileInfo, destinationFile, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return bpmProcessMapper.insertBpmProcess(bpmProcess);
    }

    @Override
    public void exportProcessFile(List<Long> processIds, HttpServletRequest request, HttpServletResponse response) throws Exception {

        List<BpmProcess> bpmProcessList = bpmProcessMapper.selectBpmProcessByProcessIds(processIds);
        String localPath = RuoYiConfig.getProfile();
        List<String> list = bpmProcessList.stream().map(m ->localPath + StringUtils.substringAfter(m.getProcessXmlPath(), Constants.RESOURCE_PREFIX)).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(list)){
            throw new Exception("请选择要下载文件/图片");
        }

        //设置响应头信息
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        //设置压缩包的名字，date为时间戳
        String date = String.valueOf(System.currentTimeMillis());
        String downloadName = "压缩包" + date + ".zip";
        //返回客户端浏览器的版本号、类型
        String agent = request.getHeader("USER-AGENT");
        try {
            //针对IE或者以IE为内核的浏览器：
            if (agent.contains("MSIE") || agent.contains("Trident")) {
                downloadName = URLEncoder.encode(downloadName, "UTF-8");
            } else {
                //非IE浏览器的处理：
                downloadName = new String(downloadName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            }
        } catch (Exception e) {
            log.error("系统异常", e);
        }

        if (list.size()>1){
            //多文件/图压缩下载
            batchFileDownLoad(list,downloadName,response);

        }else {
            //单文件/图直接下载
            singleFileDownLoad(list,bpmProcessList.get(0).getProcessName(),response);

        }

    }

    private void batchFileDownLoad(List<String> list, String downloadName, HttpServletResponse response) {
        //设置压缩包名称
        response.setHeader("Content-Disposition", "attachment;fileName=\"" + downloadName + "\"");
        //设置压缩流：直接写入response，实现边压缩边下载
        ZipOutputStream zipOs = null;
        //循环将文件写入压缩流
        DataOutputStream os = null;

        try {
            zipOs = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));
            //设置压缩方法
            zipOs.setMethod(ZipOutputStream.DEFLATED);
            //遍历文件信息（主要获取文件名/文件路径等）
            for (String t : list) {
                try {
                    //文件存储路径
                    String path = t;
                    //压缩文件中每个文件的文件名称
                    String name = t.substring(t.lastIndexOf("\\")+1);
                    log.info("batchDownloadFile:[filePath:{}]", path);
                    File file = new File(path);
                    if (!file.exists()) {
                        throw new RuntimeException("文件不存在");
                    }
                    FileInputStream fs = null;
                    try {
                        //添加ZipEntry，并将ZipEntry中写入文件流
                        zipOs.putNextEntry(new ZipEntry(name));
                        os = new DataOutputStream(zipOs);
                        fs = new FileInputStream(file);
                        byte[] b = new byte[100];
                        int length;
                        //读入需要下载的文件的内容，打包到zip文件
                        while ((length = fs.read(b)) != -1) {
                            os.write(b, 0, length);
                        }
                    } catch (Exception e) {
                        log.error("系统异常", e);
                    } finally {
                        //关闭流
                        assert fs != null;
                        fs.close();
                        zipOs.closeEntry();
                    }

                } catch (Exception e) {
                    log.error("下载文件出错![{}]", e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("系统异常", e);
        } finally {
            //关闭流
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
                if (zipOs != null) {
                    zipOs.close();
                }
            } catch (IOException e) {
                log.error("系统异常", e);
            }
        }
    }

    /**
     * 单文件直接下载
     * @param list 文件/图片路径
     * @param response
     */
    private void singleFileDownLoad(List<String> list,String processName, HttpServletResponse response) throws UnsupportedEncodingException {

        FileInputStream fis = null;
        OutputStream out = null;
        String path = list.get(0);
        String name = path.substring(path.lastIndexOf("\\")+1);
        response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(processName, StandardCharsets.UTF_8.toString())+".bpmn");
        try {
            fis = new FileInputStream(path);
            out = response.getOutputStream();
            byte[] b = new byte[1024];
            int len = 0;
            while((len = fis.read(b))!=-1){
                out.write(b, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException ex) {
                log.error("系统异常", ex);
            }
        }

    }


}
