package cn.nianxx.thhotel.integrations.document.libreoffice.adapter;

import cn.nianxx.thhotel.platform.documentconversion.service.DocumentConversionException;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

/**
 * 基于 ProcessBuilder 的 LibreOffice 进程执行器。只由 LibreOffice Adapter 使用。
 */
@Component
public class ProcessBuilderLibreOfficeProcessRunner implements LibreOfficeProcessRunner {

    /**
     * 执行 soffice 命令并处理超时；调用方负责解释退出码和输出文件。
     */
    @Override
    public LibreOfficeProcessResult run(LibreOfficeProcessRequest request) {
        ProcessBuilder processBuilder = new ProcessBuilder(request.command());
        if (request.workingDirectory() != null) {
            processBuilder.directory(request.workingDirectory().toFile());
        }
        processBuilder.redirectErrorStream(true);
        Process process = null;
        try {
            process = processBuilder.start();
            Process startedProcess = process;
            CompletableFuture<String> outputFuture = CompletableFuture.supplyAsync(
                    () -> readOutput(startedProcess.getInputStream()));
            boolean completed = process.waitFor(request.timeout().toMillis(), TimeUnit.MILLISECONDS);
            if (!completed) {
                destroyProcessTree(process);
                return new LibreOfficeProcessResult(-1, true, safeGetOutput(outputFuture), "");
            }
            return new LibreOfficeProcessResult(process.exitValue(), false, safeGetOutput(outputFuture), "");
        } catch (InterruptedException exception) {
            if (process != null) {
                destroyProcessTree(process);
            }
            Thread.currentThread().interrupt();
            throw new DocumentConversionException(
                    HttpStatus.BAD_GATEWAY,
                    "DOCUMENT_CONVERSION_FAILED",
                    "调用 LibreOffice 转换被中断。",
                    exception);
        } catch (Exception exception) {
            throw new DocumentConversionException(
                    HttpStatus.BAD_GATEWAY,
                    "DOCUMENT_CONVERSION_FAILED",
                    "调用 LibreOffice 转换失败。",
                    exception);
        }
    }

    /**
     * 超时时销毁 LibreOffice 进程树，避免 soffice 派生子进程继续占用临时目录或 CPU。
     */
    private void destroyProcessTree(Process process) {
        ProcessHandle root = process.toHandle();
        List<ProcessHandle> processTree = new ArrayList<>();
        try {
            root.descendants().forEach(processTree::add);
        } catch (RuntimeException ignored) {
            // 某些系统权限下枚举子进程可能失败；后续仍会尽力销毁根进程。
        }
        processTree.sort(Comparator.comparingLong(ProcessHandle::pid).reversed());
        processTree.forEach(this::destroyQuietly);
        destroyQuietly(root);
        waitForProcessTree(root, processTree, Duration.ofMillis(500));
        processTree.stream().filter(this::isAliveQuietly).forEach(this::destroyForciblyQuietly);
        if (isAliveQuietly(root)) {
            destroyForciblyQuietly(root);
        }
        waitForProcessTree(root, processTree, Duration.ofSeconds(1));
    }

    /**
     * 尝试发送普通销毁信号，权限或进程状态异常时静默降级。
     */
    private void destroyQuietly(ProcessHandle handle) {
        try {
            handle.destroy();
        } catch (RuntimeException ignored) {
            // 进程清理是 best-effort；异常不应覆盖原始超时或中断错误。
        }
    }

    /**
     * 尝试强制销毁进程，权限或进程状态异常时静默降级。
     */
    private void destroyForciblyQuietly(ProcessHandle handle) {
        try {
            handle.destroyForcibly();
        } catch (RuntimeException ignored) {
            // 进程清理是 best-effort；异常不应覆盖原始超时或中断错误。
        }
    }

    /**
     * 安全判断进程是否仍存活。
     */
    private boolean isAliveQuietly(ProcessHandle handle) {
        try {
            return handle.isAlive();
        } catch (RuntimeException exception) {
            return false;
        }
    }

    /**
     * 等待进程树退出；等待失败不抛出，调用方已经返回安全超时错误。
     */
    private void waitForProcessTree(ProcessHandle root, List<ProcessHandle> descendants, Duration timeout) {
        long deadline = System.nanoTime() + timeout.toNanos();
        for (ProcessHandle descendant : descendants) {
            waitForHandle(descendant, deadline);
        }
        waitForHandle(root, deadline);
    }

    /**
     * 等待单个进程退出。
     */
    private void waitForHandle(ProcessHandle handle, long deadlineNanos) {
        if (!isAliveQuietly(handle)) {
            return;
        }
        long remainingNanos = deadlineNanos - System.nanoTime();
        if (remainingNanos <= 0) {
            return;
        }
        try {
            handle.onExit().get(remainingNanos, TimeUnit.NANOSECONDS);
        } catch (Exception ignored) {
            // 超时或等待失败时继续执行后续 destroyForcibly / 清理流程。
        }
    }

    /**
     * 读取进程输出，最多保留前 4000 个字符，避免日志或内存被异常输出撑爆。
     */
    private String readOutput(InputStream inputStream) {
        try (InputStream stream = inputStream) {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] chunk = new byte[512];
            int totalKept = 0;
            int read;
            while ((read = stream.read(chunk)) >= 0) {
                if (totalKept < 4000) {
                    int keep = Math.min(read, 4000 - totalKept);
                    buffer.write(chunk, 0, keep);
                    totalKept += keep;
                }
            }
            return buffer.toString(StandardCharsets.UTF_8);
        } catch (Exception exception) {
            return "";
        }
    }

    /**
     * 获取异步输出；读取失败时返回空字符串。
     */
    private String safeGetOutput(CompletableFuture<String> outputFuture) {
        try {
            return outputFuture.get(1, TimeUnit.SECONDS);
        } catch (Exception exception) {
            return "";
        }
    }
}
