package com.yd.oss.service.utils;

import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Date;

@Slf4j
public class PdfUtil {

    /**
     * windows系统word转pdf
     * @param pdfFile 转换后的pdf文件
     * @param wordFile word源文件
     */
    public static void winWordToPdf(File pdfFile, File wordFile) {
        try {
            IConverter converter = LocalConverter.builder().build();
            converter.convert(new FileInputStream(wordFile))
                    .as(DocumentType.DOCX)
                    .to(new FileOutputStream(pdfFile))
                    .as(DocumentType.PDF).execute();
        } catch (FileNotFoundException e) {
            log.error("word转换pdf失败", e);
        }
    }

    /**
     * linux系统word转pdf
     * 使用LibreOffice转换。系统需安装LibreOffice
     * 转换命令 libreoffice --invisible --convert-to pdf --outdir output_dir source_path
     * 转换后的pdf文件名使用的是源文件的名称，所以如果要指定输出文件名称，就需把源文件名称改成想要输出的名称
     * @param pdfFile 转换后的pdf文件
     * @param wordFile word源文件
     */
    public static void linuxWordToPdf(File pdfFile, File wordFile) {
        String sourcePath = wordFile.getAbsolutePath();
        String outDir = pdfFile.getAbsolutePath().substring(0, pdfFile.getAbsolutePath().lastIndexOf(File.separator));

        log.info("PDF转换参数 - 源文件: {}, 输出目录: {}, 目标PDF: {}", sourcePath, outDir, pdfFile.getAbsolutePath());

        // 检查源文件是否存在
        if (!wordFile.exists()) {
            throw new RuntimeException("源文件不存在: " + sourcePath);
        }
        log.info("源文件存在，大小: {} bytes", wordFile.length());

        // 确保输出目录存在
        new File(outDir).mkdirs();

        // 根据源文件名预测LibreOffice的输出文件名
        String sourceFileName = wordFile.getName();
        String expectedPdfName = sourceFileName.substring(0, sourceFileName.lastIndexOf('.')) + ".pdf";
        File expectedPdfFile = new File(outDir, expectedPdfName);

        log.info("预期LibreOffice输出文件: {}", expectedPdfFile.getAbsolutePath());

        // 使用绝对路径尝试不同的命令
        String[] commands = {
                "/usr/bin/libreoffice --invisible --headless --convert-to pdf --outdir " + outDir + " " + sourcePath,
                "/usr/bin/soffice --invisible --headless --convert-to pdf --outdir " + outDir + " " + sourcePath,
                "/usr/lib64/libreoffice/program/soffice --invisible --headless --convert-to pdf --outdir " + outDir + " " + sourcePath,
                // 添加字体配置的命令
                "timeout 30s /usr/bin/libreoffice --invisible --headless --convert-to pdf:writer_pdf_Export --outdir " + outDir + " " + sourcePath,
                // 新增：指定字体目录
                "timeout 30s /usr/bin/libreoffice --invisible --headless --env:UserInstallation=file:///tmp/libreoffice --convert-to pdf --outdir " + outDir + " " + sourcePath
        };

        boolean success = false;
        Exception lastException = null;

        for (String command : commands) {
            try {
                log.info("尝试命令: {}", command);

                // 清理可能存在的预期PDF文件（避免使用旧文件）
                if (expectedPdfFile.exists()) {
                    log.warn("删除已存在的预期PDF文件: {}", expectedPdfFile.getAbsolutePath());
                    expectedPdfFile.delete();
                }

                String output = executeLinuxCmdWithOutput(command);
                log.info("命令输出: {}", output);

                // 等待文件生成，使用更可靠的等待机制
                if (waitForPdfFile(expectedPdfFile, 10)) {
                    // 检查PDF文件是否生成
                    if (expectedPdfFile.exists() && expectedPdfFile.length() > 0) {
                        log.info("LibreOffice生成PDF成功! 文件: {}, 大小: {} bytes, 使用命令: {}",
                                expectedPdfFile.getAbsolutePath(), expectedPdfFile.length(), command);

                        // 复制到目标位置
                        Files.copy(expectedPdfFile.toPath(), pdfFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                        log.info("成功复制PDF到目标位置: {}, 大小: {} bytes",
                                pdfFile.getAbsolutePath(), pdfFile.length());
                        success = true;
                        break;
                    }
                } else {
                    log.warn("命令执行成功但未生成预期PDF文件: {}", command);

                    // 检查输出目录中是否有其他PDF文件（按修改时间排序，取最新的）
                    File outDirFile = new File(outDir);
                    File[] pdfFiles = outDirFile.listFiles((dir, name) ->
                            name.toLowerCase().endsWith(".pdf") &&
                                    !name.equals(pdfFile.getName())); // 排除目标文件本身

                    if (pdfFiles != null && pdfFiles.length > 0) {
                        // 按最后修改时间排序，取最新的PDF文件
                        Arrays.sort(pdfFiles, (f1, f2) ->
                                Long.compare(f2.lastModified(), f1.lastModified()));

                        log.info("在输出目录中找到 {} 个PDF文件，按修改时间排序:", pdfFiles.length);
                        for (File f : pdfFiles) {
                            log.info(" - {} (大小: {} bytes, 修改时间: {})",
                                    f.getName(), f.length(), new Date(f.lastModified()));
                        }

                        // 使用最新的PDF文件
                        File latestPdf = pdfFiles[0];
                        if (latestPdf.length() > 0) {
                            log.info("使用最新的PDF文件: {}", latestPdf.getAbsolutePath());
                            Files.copy(latestPdf.toPath(), pdfFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                            if (pdfFile.exists() && pdfFile.length() > 0) {
                                log.info("已复制PDF文件到目标位置: {}", pdfFile.getAbsolutePath());
                                success = true;
                                break;
                            }
                        }
                    } else {
                        log.warn("输出目录中未找到任何PDF文件");
                    }
                }
            } catch (Exception e) {
                lastException = e;
                log.error("命令执行失败: {}, 错误: {}", command, e.getMessage());
            }
        }

        // 清理临时文件
        try {
            if (expectedPdfFile.exists()) {
                expectedPdfFile.delete();
                log.info("清理临时PDF文件: {}", expectedPdfFile.getAbsolutePath());
            }
        } catch (Exception e) {
            log.warn("清理临时文件失败: {}", e.getMessage());
        }

        if (!success) {
            throw new RuntimeException("所有LibreOffice转换命令都失败，源文件: " + sourcePath, lastException);
        }
    }

    /**
     * 等待PDF文件生成
     */
    private static boolean waitForPdfFile(File pdfFile, int maxWaitSeconds) {
        log.info("等待PDF文件生成: {}, 最大等待时间: {}秒", pdfFile.getAbsolutePath(), maxWaitSeconds);

        for (int i = 0; i < maxWaitSeconds * 2; i++) {
            if (pdfFile.exists()) {
                long fileSize = pdfFile.length();
                log.info("检测到PDF文件存在，当前大小: {} bytes", fileSize);

                if (fileSize > 0) {
                    // 确保文件已经完全写入
                    long size1 = pdfFile.length();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return false;
                    }
                    long size2 = pdfFile.length();
                    if (size1 == size2 && size1 > 0) {
                        log.info("PDF文件已稳定，最终大小: {} bytes", size1);
                        return true;
                    }
                }
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        log.warn("等待PDF文件生成超时，文件存在: {}, 文件大小: {} bytes",
                pdfFile.exists(), pdfFile.exists() ? pdfFile.length() : 0);
        return false;
    }

    /**
     * 执行命令行并返回输出
     */
    private static String executeLinuxCmdWithOutput(String cmd) throws Exception {
        log.info("执行命令: {}", cmd);

        Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", cmd});

        // 读取标准输出
        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        // 读取错误输出
        BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        StringBuilder output = new StringBuilder();
        StringBuilder error = new StringBuilder();

        String line;
        while ((line = inputReader.readLine()) != null) {
            output.append(line).append("\n");
            log.info("命令输出: {}", line);
        }
        while ((line = errorReader.readLine()) != null) {
            error.append(line).append("\n");
            log.error("命令错误: {}", line);
        }

        int exitCode = process.waitFor();
        log.info("命令退出码: {}", exitCode);

        if (exitCode != 0) {
            throw new RuntimeException("命令执行失败，退出码: " + exitCode + ", 错误: " + error.toString());
        }

        inputReader.close();
        errorReader.close();

        return output.toString();
    }

    /**
     * 执行命令行
     *
     * @param cmd 命令行
     * @return
     */
    private static boolean executeLinuxCmd(String cmd)  {
        try {
            Process process = Runtime.getRuntime().exec(cmd);
            process.waitFor();
        } catch (InterruptedException e) {
            log.error("executeLinuxCmd 执行Linux命令异常：", e);
            Thread.currentThread().interrupt();
            return false;
        } catch (IOException e) {
            log.error("获取系统命令执行环境异常", e);
        }
        return true;
    }

    public static void main(String[] args) {
        // 待转换的word文件
        File wordFile = new File("D:\\wjzh\\xcd.docx");
        // 转换后的pdf文件
        File pdfFile = new File("D:\\wjzh\\zhxcd.pdf");
        // 获取当前系统名称
        String osName = System.getProperty("os.name").toLowerCase();
        // 根据系统选择执行方法
        if (osName.contains("win")) {
            PdfUtil.winWordToPdf(pdfFile, wordFile);
        } else if (osName.contains("nux") || osName.contains("nix")) {
            PdfUtil.linuxWordToPdf(pdfFile, wordFile);
        }
    }
}