package com.cn.fdapfe.biz.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class ExeProcessManager {
    private static final Logger logger = LoggerFactory.getLogger(ExeProcessManager.class);
    private static final int STREAM_READ_TIMEOUT = 5000; // 流读取超时时间（毫秒）

    /**
     * 增强型EXE启动方法（支持程序运行后保持窗口）
     * @param exePath EXE文件绝对路径
     * @param keepWindow 是否保持窗口不关闭（仅Windows控制台程序有效）
     * @return 进程执行结果（包含退出码和完整日志）
     * @throws IOException 文件操作异常
     * @throws InterruptedException 线程中断异常
     */
    public static ExeResult startExe(String exePath, boolean keepWindow) throws IOException, InterruptedException {
        long startTime = System.currentTimeMillis();
        logger.info("--------------------- 开始执行EXE ---------------------");
        logger.info("EXE路径: {}", exePath);

        // 1. 基础校验
        validateExePath(exePath);
        logger.info("EXE校验通过: 存在且可执行");

        // 2. 构建命令（支持保持窗口）
        List<String> command = new ArrayList<>();
        if (keepWindow && isWindows()) {
            // Windows下通过cmd启动并保持窗口（适用于控制台程序）
            command.addAll(Arrays.asList("cmd.exe", "/k", exePath));
        } else {
            // 直接启动EXE（默认方式）
            command.add(exePath);
        }

        // 3. 初始化进程构建器
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        configureProcessBuilder(processBuilder, exePath);

        // 4. 启动进程
        logger.debug("启动命令: {}", command);
        Process process = processBuilder.start();
        logger.info("进程启动成功 (PID: {})", process);

        // 5. 流处理线程（实时打印输出）
        StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
        StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "STDERR");
        Thread outputThread = new Thread(outputGobbler, "EXE-Output-Gobbler");
        Thread errorThread = new Thread(errorGobbler, "EXE-Error-Gobbler");
        outputThread.start();
        errorThread.start();

        // 6. 等待进程终止（保持窗口时不主动等待，由用户手动关闭）
        if (!keepWindow) {
            int exitCode = process.waitFor();
            logger.info("进程执行完毕 (耗时: {}ms)", System.currentTimeMillis() - startTime);
            outputThread.join(STREAM_READ_TIMEOUT);
            errorThread.join(STREAM_READ_TIMEOUT);
            return new ExeResult(exitCode, outputGobbler.getLog(), errorGobbler.getLog());
        } else {
            // 保持窗口模式：不阻塞主线程，返回空结果（进程在后台运行）
            logger.info("EXE以窗口保持模式启动，手动关闭窗口后进程终止");
            return new ExeResult(-1, "", ""); // 退出码-1表示进程仍在运行
        }
    }

    private static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().startsWith("win");
    }

    private static void validateExePath(String exePath) {
        File exeFile = new File(exePath);
        if (!exeFile.exists()) {
            logger.error("EXE文件不存在: {}", exePath);
            throw new IllegalArgumentException("EXE文件不存在: " + exePath);
        }
        if (!exeFile.canExecute()) {
            logger.error("缺少EXE执行权限: {}", exePath);
            throw new SecurityException("缺少EXE执行权限: " + exePath);
        }
    }

    private static void configureProcessBuilder(ProcessBuilder processBuilder, String exePath) {
        // 设置工作目录为EXE所在目录
        File exeDir = new File(exePath).getParentFile();
        processBuilder.directory(exeDir != null ? exeDir : new File("."));
        // 继承完整环境变量
        processBuilder.environment().putAll(System.getenv());
    }

    /**
     * 流处理内部类（实时打印+日志收集）
     */
    private static class StreamGobbler implements Runnable {
        private final InputStream inputStream;
        private final String streamType;
        private final StringBuilder logBuffer = new StringBuilder();

        public StreamGobbler(InputStream inputStream, String streamType) {
            this.inputStream = inputStream;
            this.streamType = streamType;
        }

        @Override
        public void run() {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    logger.info("[{}] [{}] {}: {}",
                            new Date().toString(),
                            Thread.currentThread().getName(),
                            streamType,
                            line
                    );
                    logBuffer.append(line).append(System.lineSeparator());
                }
            } catch (IOException e) {
                logger.error("{}流读取失败: {}", streamType, e.getMessage(), e);
            }
        }

        public String getLog() {
            return logBuffer.toString();
        }
    }

    /**
     * 执行结果封装类
     */
    public static class ExeResult {
        private final int exitCode;
        private final String stdoutLog;
        private final String stderrLog;

        public ExeResult(int exitCode, String stdoutLog, String stderrLog) {
            this.exitCode = exitCode;
            this.stdoutLog = stdoutLog;
            this.stderrLog = stderrLog;
        }

        // Getter方法
        public int getExitCode() { return exitCode; }
        public String getStdoutLog() { return stdoutLog; }
        public String getStderrLog() { return stderrLog; }
    }
}
