package cn.keking.service;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.keking.config.ConfigConstants;
import cn.keking.config.KkfileConfig;
import cn.keking.exception.KkFileException;
import cn.keking.service.impl.OtherFilePreviewImpl;
import cn.keking.utils.Consts;
import cn.keking.utils.WebUtils;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.HtmlUtils;

import javax.annotation.Resource;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author Hikaru
 */
@Service
@Slf4j
public class ContractService {


    /**
     * 配置常量
     */
    @Resource
    private ConfigConstants configConstants;


    /**
     * kkfile配置
     */
    @Resource
    private KkfileConfig kkfileConfig;


    private final OfficeToPdfService officeToPdfService;


    public ContractService(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService, OtherFilePreviewImpl otherFilePreview) {
        this.officeToPdfService = officeToPdfService;

    }

    /**
     * 上传
     *
     * @param templateName  模板名
     * @param multipartFile 模板文件
     * @return 路径
     * @throws IOException 异常
     */
    public String upload(String templateName, MultipartFile multipartFile) {
        if (multipartFile == null) {
            throw new KkFileException("上传文件不能为空");
        }
        // 获取文件名
        String fileName = multipartFile.getOriginalFilename();
        fileName = HtmlUtils.htmlEscape(fileName, StandardCharsets.UTF_8.name());

        //获取文件大小 ，文件大小不超过10m
        long size = multipartFile.getSize();
        if (size > kkfileConfig.getTemplateMaxSize()) {
            long currentSizeM = kkfileConfig.getTemplateMaxSize() / 1024 / 1024;
            throw new KkFileException("上传的文件限制大小为" + currentSizeM + "m");
        }
        //获取文件扩展名，目前模板只支持docx
        String extensionName = getExtension(fileName);
        if (!kkfileConfig.getAllowUploadType().contains(extensionName)) {
            throw new KkFileException("不支持的文件类型【" + extensionName + "】");
        }
        //目录不存在重新生成目录
        File outFile = new File(Consts.getTemplatePath());

        if (!outFile.exists() && !outFile.mkdirs()) {
            log.error("创建文件夹【{}】失败，请检查目录权限！", Consts.getTemplatePath());
        }
        String newFileName = templateName + "." + extensionName;
        String url = this.configConstants.getBaseUrl() + "/" + Consts.TEMPLATE_PATH_NAME + "/" + templateName + "." + extensionName;
        String filePath = Consts.getTemplatePath() + File.separator + newFileName;
        log.info("上传文件：{}", filePath);
        try (InputStream in = multipartFile.getInputStream(); OutputStream out = new FileOutputStream(filePath)) {
            StreamUtils.copy(in, out);
        } catch (Exception e) {
            log.error("文件拷贝失败:", e);
            throw new KkFileException("文件拷贝失败");
        }
        return url;
    }

    /**
     * 查询所有模板url
     *
     * @param templateNames 模板名称
     * @return 模板url
     */
    public List<String> listTemplate(List<String> templateNames) {
        return templateNames.stream().map(s -> downUrl(s, "docx")).collect(Collectors.toList());
    }

    private String downUrl(String templateName, String extensionName) {
        return this.configConstants.getBaseUrl() + "/" + Consts.TEMPLATE_PATH_NAME + "/" + templateName + "." + extensionName;
    }

    /**
     * 根据模板生成内容
     *
     * @param templateName 模板名称
     * @param dataMap      数据
     * @return 生成得预览路径
     * @throws Exception 异常
     */
    public String gen(String templateName, Map<String, Object> dataMap) throws Exception {
        OutputStream os = null;
        try {
            File outFile = new File(Consts.uploadAbsDir());
            if (!outFile.exists()) {
                outFile.mkdirs();
            }
            String templatePath = Consts.getTemplatePath();
            String abTemplatePath = templatePath + File.separator + templateName + ".docx";
            //判断模板是否存在
            File file = new File(abTemplatePath);
            if (!file.exists()) {
                throw new KkFileException("请先上传模板!");
            }
            XWPFDocument xwpfDocument = WordExportUtil.exportWord07(abTemplatePath, dataMap);
            //如果是签收单，则需要合并单元格
            if (Consts.RECEIPT_FORM.equals(templateName)){
                List<Object> invoiceResList = (List<Object>) MapUtils.getObject(dataMap, "invoiceResList");
                mergeCell(xwpfDocument,0,7,7+invoiceResList.size()-1,Arrays.asList(5,6));
            }
            if (Consts.RECEIPT_INVOICE.equals(templateName)){
                List<Object> invoiceResList = (List<Object>) MapUtils.getObject(dataMap, "invoiceResList");
                mergeCell(xwpfDocument,0,7,7+invoiceResList.size()-1,Arrays.asList(5,6));
            }

            String docxFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".docx";
            //通过填参 生产文件 并放到 upload 文件夹中
            String path = Consts.uploadAbsDir();
            String pathFile = path + docxFile;
            os = new FileOutputStream(pathFile);
            xwpfDocument.write(os);
            log.info("文件路径：{}", kkfileConfig.getLocalfileUrl() + File.separator + Consts.UPLOAD_PATH_NAME + docxFile);
            String previewUrl = WebUtils.getBaseUrl() + "onlinePreview?url=" +
                    URLEncoder.encode(Base64.encodeBase64String((kkfileConfig.getLocalfileUrl() + File.separator + Consts.UPLOAD_PATH_NAME + docxFile).getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8.name());
            return previewUrl;
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }

    /**
     *  合并单元格
     * @param xwpfDocument  文档对象
     * @param tableIndex    表格索引
     * @param beginRowIndex 开始行索引
     * @param endRowIndex   结束行索引
     * @param collIndexs    合并列索引
     */
    public static void mergeCell(XWPFDocument xwpfDocument,int tableIndex,int beginRowIndex,int endRowIndex,List<Integer> collIndexs){
        //获取文档中指定的表格对象
        XWPFTable table = xwpfDocument.getTableArray(tableIndex);
        if (beginRowIndex == endRowIndex || beginRowIndex > endRowIndex){
            return;
        }
        //合并单元格的第一个单元格
        CTVMerge startMerge = CTVMerge.Factory.newInstance();
        startMerge.setVal(STMerge.RESTART);
        //合并行单元格之后的单元格
        CTVMerge endMerge = CTVMerge.Factory.newInstance();
        endMerge.setVal(STMerge.CONTINUE);

        for (Integer collIndex : collIndexs){
            table.getRow(beginRowIndex).getCell(collIndex).getCTTc().getTcPr().setVMerge(startMerge);
            for (int i = beginRowIndex + 1; i <= endRowIndex; i++){
                table.getRow(i).getCell(collIndex).getCTTc().getTcPr().setVMerge(endMerge);
            }
        }
    }

    /**
     * 获取扩展名
     *
     * @param fileName 文件名
     * @return 扩展名
     */
    public static final String getExtension(String fileName) {
        return FilenameUtils.getExtension(fileName);
    }

    /**
     * 获取绝对路径
     *
     * @param uploadDir 上传得目录
     * @param fileName  文件
     * @return 获取文件绝对路径
     * @throws IOException 异常
     */
    public File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.exists()) {
            if (!desc.getParentFile().exists()) {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }

    /**
     * 返回 pdf 文件
     * @param templateName
     * @param dataMap
     * @return
     * @throws Exception
     */

    public File download(String templateName, Map<String, Object> dataMap,String type) throws Exception {
        OutputStream os = null;
        try {
            File outFile = new File(Consts.uploadAbsDir());
            if (!outFile.exists()) {
                outFile.mkdirs();
            }
            String templatePath = Consts.getTemplatePath();
            String abTemplatePath = templatePath + File.separator + templateName + ".docx";
            //判断模板是否存在
            File file = new File(abTemplatePath);
            if(!file.exists()){
                throw new KkFileException("请先上传模板!");
            }
            XWPFDocument xwpfDocument = WordExportUtil.exportWord07(abTemplatePath, dataMap);
            //如果是签收单，则需要合并单元格
            if (Consts.RECEIPT_FORM.equals(templateName)){
                List<Object> invoiceResList = (List<Object>) MapUtils.getObject(dataMap, "invoiceResList");
                mergeCell(xwpfDocument,0,7,7+invoiceResList.size()-1,Arrays.asList(5,6));
            }
            String docxFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".docx";
            String pdfName = "/"+ UUID.randomUUID().toString().replace("-", "") + ".pdf";
            //通过填参 生产文件 并放到 upload 文件夹中
            String path = Consts.uploadAbsDir();
            String pathFile = path + docxFile;
            os = new FileOutputStream(pathFile);
            xwpfDocument.write(os);
            String outFilePath = Consts.uploadAbsDir()+ File.separator+ pdfName;
            officeToPdfService.openOfficeToPDF(pathFile, outFilePath);
            if("pdf".equals(type)){
                //返回pdf
                return  new File(outFilePath);
            }
            //返回word
            return  new File(pathFile);
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }
    public File downloadExcel(String templateName, Map<String, Object> dataMap) throws Exception {
        OutputStream os = null;
        try {
            File outFile = new File(Consts.uploadAbsDir());
            if (!outFile.exists()) {
                outFile.mkdirs();
            }
            String templatePath = Consts.getTemplatePath();
            String abTemplatePath = templatePath + File.separator + templateName + ".xlsx";
            //判断模板是否存在
            File file = new File(abTemplatePath);
            if(!file.exists()){
                throw new KkFileException("请先上传模板!");
            }

            String excelFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".xlsx";
            //通过填参 生产文件 并放到 upload 文件夹中
            String path = Consts.uploadAbsDir();
            String pathFile = path + excelFile;
            os = new FileOutputStream(pathFile);
            TemplateExportParams params = new TemplateExportParams(
                    abTemplatePath);
            Workbook workbook = ExcelExportUtil.exportExcel(params, dataMap);
            workbook.write(os);
            return  new File(pathFile);
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }



    /**
     * 返回 word 文件
     * @param templateName
     * @param dataMap
     * @return
     * @throws Exception
     */

    public File downloadWord(String templateName, Map<String, Object> dataMap) throws Exception {
        OutputStream os = null;
        try {
            File outFile = new File(Consts.uploadAbsDir());
            if (!outFile.exists()) {
                outFile.mkdirs();
            }
            String templatePath = Consts.getTemplatePath();
            String abTemplatePath = templatePath + File.separator + templateName + ".docx";
            //判断模板是否存在
            File file = new File(abTemplatePath);
            if(!file.exists()){
                throw new KkFileException("请先上传模板!");
            }
            XWPFDocument xwpfDocument = WordExportUtil.exportWord07(abTemplatePath, dataMap);
            //如果是签收单，则需要合并单元格
            if (Consts.RECEIPT_FORM.equals(templateName)){
                List<Object> invoiceResList = (List<Object>) MapUtils.getObject(dataMap, "invoiceResList");
                mergeCell(xwpfDocument,0,7,7+invoiceResList.size()-1,Arrays.asList(5,6));
            }
            String docxFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".docx";
            //通过填参 生产文件 并放到 upload 文件夹中
            String path = Consts.uploadAbsDir();
            String pathFile = path + docxFile;
            os = new FileOutputStream(pathFile);
            xwpfDocument.write(os);
            //返回word
            return  new File(pathFile);
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }

    public String excel(String templateName, Map<String, Object> dataMap) throws Exception  {
        OutputStream os = null;
        try {
            File outFile = new File(Consts.uploadAbsDir());
            if (!outFile.exists()) {
                outFile.mkdirs();
            }
            String templatePath = Consts.getTemplatePath();
            String abTemplatePath = templatePath + File.separator + templateName + ".xlsx";
            //判断模板是否存在
            File file = new File(abTemplatePath);
            if (!file.exists()) {
                throw new KkFileException("请先上传模板!");
            }
            TemplateExportParams params = new TemplateExportParams(
                    abTemplatePath);
            Workbook workbook = ExcelExportUtil.exportExcel(params, dataMap);
            String docxFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".xlsx";
            //通过填参 生产文件 并放到 upload 文件夹中
            String path = Consts.uploadAbsDir();
            String pathFile = path + docxFile;
            os = new FileOutputStream(pathFile);
            workbook.write(os);
            log.info("文件路径：{}", kkfileConfig.getLocalfileUrl() + File.separator + Consts.UPLOAD_PATH_NAME + docxFile);
            String previewUrl = WebUtils.getBaseUrl() + "onlinePreview?url=" +
                    URLEncoder.encode(Base64.encodeBase64String((kkfileConfig.getLocalfileUrl() + File.separator + Consts.UPLOAD_PATH_NAME + docxFile).getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8.name());
            return previewUrl;
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }

}
