const fs = require('fs')
const path = require('path')
const compressing = require('compressing')
const rimrif = require('rimraf')
const shell = require('shelljs');

const crypto = require('crypto');

// 文档模板生成
const PizZip = require("pizzip");
const Docxtemplater = require("docxtemplater");

// 文档转换
import { Chromiumly } from "chromiumly";
// Chromiumly.configure({ endpoint: "http://8.140.98.43/docs" });
Chromiumly.configure({ endpoint: "http://123.57.204.89/docs" });
const { LibreOffice } = require("chromiumly");
const { PDFEngines } = require("chromiumly");
const tempDir = path.join(__dirname , "temp");
if(!fs.existsSync(tempDir)){fs.mkdirSync(tempDir)};

const OSS = require("ali-oss");
const ALI_OSS_BUCKET = process.env.ALI_OSS_BUCKET || "hep-textbook"
const ALI_OSS_ACCESS_KEY_ID = process.env.ALI_OSS_ACCESS_KEY_ID || "LTAI5t6AbTiAvXmeoVdJZhL3"
const ALI_OSS_ACCESS_KEY_SECRET = process.env.ALI_OSS_ACCESS_KEY_SECRET || "KLtQRdIW69KLP7jnzHNUf7eKmdptxH"

const bwipjs = require("bwip-js")
export async function toBarCode(text){
    return new Promise(resolve=>{
        bwipjs.toBuffer({
            bcid:"code128",
            text:text,
            scale:1.5,
            height:3,
            includetext:false,
            textalign:"center"
        },(err,png)=>{
            if(err){
                console.error(err)
                resolve(null)
            }else{
                resolve(png)
            }
        })
    })
}

export async function uploadFileToOSS(filePath){
    let client = new OSS({
        // yourRegion填写Bucket所在地域。以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。
        region: "oss-cn-beijing",
        accessKeyId: ALI_OSS_ACCESS_KEY_ID,
        accessKeySecret: ALI_OSS_ACCESS_KEY_SECRET,
        // 填写Bucket名称。
        bucket: ALI_OSS_BUCKET || "hep-textbook",
    });

    let now = new Date();
    let fileName = getFileName(filePath);
    let fileKey = `export/report/${fileName}`;
    const r1 = await client?.put(fileKey, filePath);
    console.log('put success: %j', r1);
    return r1
}
export function getFileName(filePath) {
    // 使用 '/' 或 '\' 作为分隔符,分割路径
    const parts = filePath.split(/[/\\]/);
    // 返回最后一个部分,即文件名
    return parts.pop();
}
module.exports.uploadFileToOSS = uploadFileToOSS

/**
 * 将给定的文件路径数组打包成指定名称的zip压缩包
 * @param {Array<string>} filePathList - 要打包的文件路径数组
 * @param {string} outputZipName - 输出的zip文件名称
 */
 export function createZip(filePathList, outputZipName) {
    let zipStream = new compressing.zip.Stream();
    return new Promise((resolve)=>{
        try {
            let outputPath = path.join(tempDir,outputZipName)
            // 遍历文件路径列表,将每个文件添加到zip流中
            for (const filePath of filePathList) {
                // 检查文件是否存在
                if (fs.existsSync(filePath)) {
                    // 将文件添加到zip流中
                    zipStream.addEntry(filePath);
                } else {
                    console.error(`文件不存在: ${filePath}`);
                }
            }
            
            // 创建一个写入流
            const output = fs.createWriteStream(outputPath);
            
            // 使用 compressing 库的 zip 方法将文件打包
            // console.log(filePathList)
            // await compressing.zip.compressDir(filePathList, output);
            // 将zip流写入文件
            zipStream.pipe(output);

            output.on('finish', () => {
                // console.log(`成功创建压缩包: ${outputPath}`);
                resolve(outputPath)
            });

            output.on('error', (error) => {
                console.error('写入压缩包时出错:', error);
                resolve(null)
            });

            // console.log(`成功创建压缩包: ${outputPath}`);
            // return outputPath
        } catch (error) {
            console.error('创建压缩包时出错:', error);
            return null
        }
    })

}
module.exports.createZip = createZip


const download = require('download')
async function downloadUrl(url) {
    let md5 = crypto.createHash('md5');
    let filename = md5.update(url).digest('hex') + path.extname(url)
    let filepath = path.join(tempDir,filename)
    // console.log(filename,filepath)
    try{
        if(fs.existsSync(filepath)){fs.rmSync(filepath)}
        fs.writeFileSync(filepath, await download(url));
        return filepath
    }catch(err){
        console.error(err)
        return null
    }
}


/**
 * 将 DOCX 文件转换为 PDF
 * 
 * @param {string} docxPath - 要转换的 DOCX 文件的路径
 * @param {string} outputPath - 输出 PDF 文件的路径
 * @returns {Promise<void>}
 */
 export async function docxToPdf(docxPath, outputPath,options) {
    let mergeFiles = options?.mergeFiles || []
    let filePathList = []
    let merge = false;
    if(mergeFiles?.length){
        let plist = []
        for (let index = 0; index < mergeFiles.length; index++) {
            let filePath
            plist.push((async ()=>{
                try{
                    filePath = await downloadUrl(mergeFiles[index]);
                }catch(err){}
                if(filePath){
                    filePathList.push(filePath)
                }
                return
            })())
        }
        await Promise.all(plist);
        merge = true;
    }
    console.log("DOWNLOADED:",filePathList)
    try {
        let docxBuffer = fs.readFileSync(docxPath);

        let files = [
            // docxPath
            { data: docxBuffer, ext: "docx" },
            ...filePathList
        ];
        // console.log(files)
        let pdfBuffer = await LibreOffice.convert({
            files,
            properties: {
                // 设置页面属性,例如纸张大小和方向
                pageSize: 'A4',
                orientation: 'portrait',
                margin: {
                    top: 0,
                    right: 0,
                    bottom: 0,
                    left: 0
                }
            },
            pdfa: false, // 根据需要设置
            pdfUA: false, // 根据需要设置
            merge: merge, // 如果只转换一个文件,设置为false
            // metadata: {
            //     // 你可以在这里添加元数据
            // },
            // losslessImageCompression: false,
            // reduceImageResolution: false,
            // quality: 90, // JPG 导出质量
            // maxImageResolution: 300 // 最大图像分辨率
        });

        // 将 Buffer 写入输出文件
        fs.writeFileSync(outputPath, pdfBuffer);
        console.log(`成功输出 ${outputPath}`);
        return outputPath
    } catch (error) {
        console.error('转换失败:', error);
        return null
    }
}
module.exports.docxToPdf = docxToPdf

const ImageModule = require("@slosarek/docxtemplater-image-module-free");
const sizeOf = require("image-size");


export function renderDocx(inputDocxPath, outputDocxName, options){
    let imageOptions = {
        getImage(tagValue,tagName) {
            if(!fs.existsSync(tagValue)){
                throw new Error(`Image not found: ${tagValue}`);
            }
            return fs.readFileSync(tagValue);
        },
        getSize(img) {
            const sizeObj = sizeOf(img);
            console.log(sizeObj);
            return [sizeObj.width, sizeObj.height];
        },
    };
    
    let outputDocxPath = path.join(tempDir,outputDocxName)
    // Load the docx file as binary content
    let content = fs.readFileSync(
        inputDocxPath,
        "binary"
    );

    // Unzip the content of the file
    let zip = new PizZip(content);
    let doc = new Docxtemplater(zip, {
        paragraphLoop: true,
        linebreaks: true,
        modules: [new ImageModule(imageOptions)],
    });

    // Render the document (Replace {first_name} by John, {last_name} by Doe, ...)
    Object.keys(options).forEach(key=>{ // 除去空值
        if(options[key]==undefined){
            options[key] = ""
        }
    })
    doc.render(options);

    // Get the zip document and generate it as a nodebuffer
    let buf = doc.getZip().generate({
        type: "nodebuffer",
        // compression: DEFLATE adds a compression step.
        // For a 50MB output document, expect 500ms additional CPU time
        compression: "DEFLATE",
    });

    // buf is a nodejs Buffer, you can either write it to a
    // file or res.send it with express for example.
    fs.writeFileSync(outputDocxPath, buf);
    return outputDocxPath
}
/**
 * docx 替换模板字符串内容
 * @example
    // 要替换内容的模板
    let inputDocx = 'cs.docx'
    // 替换完成的docx文件
    let outputDocx = 'dd.docx'
    // {{xx}} 处要替换的内容
    let replaceData = {
        name: '替换name处的内容',
        age: '替换age处的内容',
    }
    replaceDocx(inputDocx, outputDocx, replaceData)
 */
export function replaceDocx(inputDocxPath, outputDocxPath, options,eventMap) {
    return new Promise((resolve,reject)=>{

    // 解压出来的临时目录
    let md5 = crypto.createHash('md5');
    let outmd5 = md5.update(outputDocxPath).digest('hex')
    let tempDocxPath =  path.join(tempDir , outmd5)
    // 要替换的xml文件位置
    let tempDocxXMLName = path.join(tempDocxPath,`word/document.xml`)

    // 压缩文件夹为文件
    let dir_to_docx = (inputFilePath, outputFilePath) => {
        outputFilePath = path.join(tempDir,outputFilePath)
        // 创建压缩流
        let zipStream = new compressing.zip.Stream()
        // 写出流
        let outStream = fs.createWriteStream(outputFilePath)
        fs.readdir(inputFilePath, null, (err, files) => {
            if (!err) {
                files.map(file => path.join(inputFilePath, file))
                    .forEach(file => {
                        zipStream.addEntry(file)
                    })
            }
        })
        // 写入文件内容
        zipStream.pipe(outStream)
            .on('close', () => {
                // 打包完成后删除临时目录
                // console.log(tempDocxPath)
                eventMap["onDocxComplete"]&&eventMap["onDocxComplete"](outputFilePath)
                shell.rm("-r",tempDocxPath)
                // rimrif.rimrafSync(tempDocxPath)
                resolve(true)
            })
    }

    // 替换word/document.xml文件中{{xx}}处的内容
    let replaceXML = (data, text) => {
        Object.keys(data).forEach(key => {
            text = text.replaceAll(`{{${key}}}`, data[key])
        })
        return text
    }

    // 解压docx文件替换内容重新打包成docx文件
    compressing.zip.uncompress(inputDocxPath, tempDocxPath)
        .then(() => {
            // 读写要替换内容的xml文件
            fs.readFile(tempDocxXMLName, null, (err, data) => {
                if (!err) {
                    let text = data.toString()
                    text = replaceXML(options, text)
                    fs.writeFile(tempDocxXMLName, text, (err) => {
                        if (!err) {
                            dir_to_docx(tempDocxPath, outputDocxPath)
                        } else {
                            reject(err)
                        }
                    })
                } else {
                    reject(err)
                }
            })
        }).catch(err => {
            reject(err)
        })
    })

}

module.exports.replaceDocx = replaceDocx
function generateObjectId(inputString) {
    inputString = inputString || ""
    inputString = String(inputString)
    const hash = crypto.createHash('sha256').update(inputString).digest('hex');
    const objectId = hash;
    return objectId;
}