package com.example.demo.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * 视频拼接服务
 * 用于将多个视频文件拼接成一个视频
 */
@Service
public class VideoConcatService {

    private static final Logger logger = LoggerFactory.getLogger(VideoConcatService.class);

    @Value("${app.temp.dir:./temp}")
    private String tempDir;

    @Value("${app.ffmpeg.path:ffmpeg}")
    private String ffmpegPath;

    private Boolean ffmpegAvailable = null; // 缓存FFmpeg可用性检测结果
    
    /**
     * 规范化FFmpeg路径（处理Windows路径中的反斜杠和换行符问题）
     */
    private String normalizeFFmpegPath(String path) {
        if (path == null || path.isEmpty()) {
            return "ffmpeg";
        }
        // 移除所有换行符、回车符和多余空格
        String normalized = path.replaceAll("[\\r\\n]", "").trim();
        // 将正斜杠转换为反斜杠（Windows需要）
        normalized = normalized.replace("/", "\\");
        // 移除多余的反斜杠
        normalized = normalized.replaceAll("\\\\+", "\\\\");
        return normalized;
    }

    /**
     * 下载视频文件
     * @param videoUrl 视频URL
     * @param outputPath 输出文件路径
     * @return 是否下载成功
     */
    public boolean downloadVideo(String videoUrl, String outputPath) {
        if (videoUrl == null || videoUrl.isEmpty()) {
            logger.error("视频URL为空，无法下载");
            return false;
        }

        try {
            // 创建临时目录
            Path tempPath = Paths.get(tempDir);
            if (!Files.exists(tempPath)) {
                Files.createDirectories(tempPath);
            }

            // 下载视频
            URI uri = new URI(videoUrl);
            URL url = uri.toURL();
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(30000); // 30秒连接超时
            connection.setReadTimeout(60000);   // 60秒读取超时
            connection.setRequestMethod("GET");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0");

            try (InputStream in = connection.getInputStream();
                 FileOutputStream out = new FileOutputStream(outputPath)) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            } finally {
                connection.disconnect();
            }

            logger.info("视频下载成功: {} -> {}", videoUrl, outputPath);
            return true;
        } catch (Exception e) {
            logger.error("下载视频失败: {}", videoUrl, e);
            return false;
        }
    }

    /**
     * 检测FFmpeg是否可用（公共方法，可用于测试）
     * @return true如果FFmpeg可用
     */
    public boolean isFFmpegAvailable() {
        if (ffmpegAvailable != null) {
            return ffmpegAvailable;
        }

        try {
            // 规范化路径
            String normalizedPath = normalizeFFmpegPath(ffmpegPath);
            logger.debug("FFmpeg路径（原始）: {}", ffmpegPath);
            logger.debug("FFmpeg路径（规范化后）: {}", normalizedPath);
            
            ProcessBuilder processBuilder = new ProcessBuilder(normalizedPath, "-version");
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            
            // 等待最多3秒
            boolean finished = process.waitFor(3, java.util.concurrent.TimeUnit.SECONDS);
            
            if (finished && process.exitValue() == 0) {
                ffmpegAvailable = true;
                logger.info("FFmpeg检测成功，路径: {}", normalizedPath);
                return true;
            } else {
                if (!finished) {
                    process.destroyForcibly();
                }
                ffmpegAvailable = false;
                logger.warn("FFmpeg检测失败，退出码: {}", finished ? process.exitValue() : "超时");
                return false;
            }
        } catch (Exception e) {
            ffmpegAvailable = false;
            logger.warn("FFmpeg不可用: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 拼接多个视频文件
     * 使用FFmpeg进行视频拼接（必须安装FFmpeg）
     * 注意：不再使用JCodec作为后备方案，因为会导致内存溢出
     * @param videoFiles 视频文件路径列表
     * @param outputPath 输出文件路径
     * @return 是否拼接成功
     */
    public boolean concatVideos(List<String> videoFiles, String outputPath) {
        if (videoFiles == null || videoFiles.isEmpty()) {
            logger.error("视频文件列表为空，无法拼接");
            return false;
        }

        // 首先检测FFmpeg是否可用
        if (isFFmpegAvailable()) {
            // 尝试使用FFmpeg（更快，不重新编码）
            if (tryFFmpegConcat(videoFiles, outputPath)) {
                return true;
            }
            // 如果FFmpeg调用失败，重置可用性标志以便下次重新检测
            ffmpegAvailable = null;
            logger.error("FFmpeg执行失败，无法拼接视频");
        } else {
            logger.error("FFmpeg不可用，无法拼接视频。请确保FFmpeg已正确安装并配置");
        }

        // 不再使用JCodec作为后备方案，因为：
        // 1. JCodec需要将所有视频帧加载到内存，会导致内存溢出
        // 2. JCodec的拼接功能实现不完整，无法正确输出视频
        // 3. 对于多个视频文件，内存消耗会非常大（每个视频可能有数百帧）
        logger.error("视频拼接失败：FFmpeg不可用或执行失败。请检查FFmpeg安装和配置");
        return false;
    }

    /**
     * 尝试使用FFmpeg进行视频拼接（快速，不重新编码）
     */
    private boolean tryFFmpegConcat(List<String> videoFiles, String outputPath) {
        try {
            // 创建临时目录
            Path tempPath = Paths.get(tempDir);
            if (!Files.exists(tempPath)) {
                Files.createDirectories(tempPath);
            }

            // 创建FFmpeg concat文件列表
            String concatListFile = tempDir + File.separator + "concat_" + UUID.randomUUID().toString() + ".txt";
            
            try (FileOutputStream fos = new FileOutputStream(concatListFile)) {
                for (String videoFile : videoFiles) {
                    // FFmpeg concat格式：file 'path/to/video.mp4'
                    // 使用相对路径（相对于concat文件所在目录）
                    // 由于所有视频文件都在同一个临时目录，相对路径就是文件名
                    File videoFileObj = new File(videoFile);
                    String relativePath = videoFileObj.getName(); // 直接使用文件名作为相对路径
                    
                    // 将Windows路径中的反斜杠转换为正斜杠（FFmpeg在Windows上也支持正斜杠）
                    String normalizedFile = relativePath.replace("\\", "/");
                    String line = "file '" + normalizedFile + "'\n";
                    fos.write(line.getBytes("UTF-8"));
                    logger.debug("添加到concat列表: {} (原始文件: {})", normalizedFile, videoFile);
                }
            }
            
            logger.info("FFmpeg concat文件已创建: {}, 包含 {} 个视频", concatListFile, videoFiles.size());

            // 规范化FFmpeg路径
            String normalizedPath = normalizeFFmpegPath(ffmpegPath);
            
            // 输出路径保持原样（相对路径或绝对路径都可以）
            String normalizedOutputPath = outputPath;
            
            // concat文件路径（相对于工作目录）
            File concatFileObj = new File(concatListFile);
            String concatFileName = concatFileObj.getName();
            
            // 执行FFmpeg拼接命令
            ProcessBuilder processBuilder = new ProcessBuilder(
                normalizedPath,
                "-f", "concat",
                "-safe", "0",
                "-i", concatFileName,  // 使用文件名（相对路径）
                "-c", "copy",
                "-y", // 覆盖输出文件
                normalizedOutputPath
            );

            // 设置工作目录为临时目录（这样concat文件和视频文件都在这里）
            // 使用规范化路径：如果是相对路径，会基于应用运行目录解析；如果是绝对路径，直接使用
            File tempDirFile;
            if (new File(tempDir).isAbsolute()) {
                // 已经是绝对路径，直接使用
                tempDirFile = new File(tempDir);
            } else {
                // 相对路径，转换为绝对路径（基于应用运行目录）
                tempDirFile = new File(tempDir).getAbsoluteFile();
            }
            processBuilder.directory(tempDirFile);
            logger.debug("FFmpeg工作目录: {}, concat文件: {}", tempDirFile.getAbsolutePath(), concatFileName);
            
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            // 异步读取FFmpeg输出（用于调试）
            StringBuilder output = new StringBuilder();
            Thread outputReader = new Thread(() -> {
                try (java.io.BufferedReader reader = new java.io.BufferedReader(
                        new java.io.InputStreamReader(process.getInputStream(), java.nio.charset.StandardCharsets.UTF_8))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        synchronized (output) {
                            output.append(line).append("\n");
                            // 只记录前1000字符，避免日志过长
                            if (output.length() > 1000) {
                                break;
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.debug("读取FFmpeg输出时出错: {}", e.getMessage());
                }
            });
            outputReader.setDaemon(true);
            outputReader.start();

            // 等待进程完成（最多等待10分钟，因为6个视频拼接可能需要更长时间）
            boolean finished = process.waitFor(10, java.util.concurrent.TimeUnit.MINUTES);
            
            // 等待输出读取线程完成（最多1秒）
            try {
                outputReader.join(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            // 清理临时文件
            try {
                Files.deleteIfExists(Paths.get(concatListFile));
            } catch (Exception e) {
                logger.warn("删除临时文件失败: {}", concatListFile, e);
            }

            if (finished && process.exitValue() == 0) {
                // 验证输出文件是否存在
                File outputFile = new File(normalizedOutputPath);
                if (outputFile.exists() && outputFile.length() > 0) {
                    logger.info("使用FFmpeg视频拼接成功: {} 个视频 -> {} (大小: {} bytes)", 
                        videoFiles.size(), normalizedOutputPath, outputFile.length());
                    return true;
                } else {
                    logger.error("FFmpeg执行成功但输出文件不存在或为空: {}", normalizedOutputPath);
                    return false;
                }
            } else {
                String outputStr;
                synchronized (output) {
                    outputStr = output.toString();
                }
                if (!finished) {
                    process.destroyForcibly();
                    logger.error("FFmpeg进程超时（10分钟），输出: {}", 
                        outputStr.length() > 0 ? outputStr.substring(0, Math.min(500, outputStr.length())) : "无输出");
                } else {
                    int exitCode = process.exitValue();
                    logger.error("FFmpeg退出码: {}, 输出: {}", exitCode, 
                        outputStr.length() > 0 ? outputStr.substring(0, Math.min(500, outputStr.length())) : "无输出");
                }
                return false;
            }
        } catch (Exception e) {
            logger.debug("FFmpeg不可用或执行失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 使用JCodec进行视频拼接（已禁用）
     * 
     * 注意：此方法已被禁用，原因：
     * 1. JCodec需要将所有视频帧加载到内存，会导致OutOfMemoryError
     * 2. 对于多个视频文件（如6个视频），内存消耗会非常大
     * 3. JCodec的拼接功能实现不完整，无法正确输出视频
     * 
     * 解决方案：必须使用FFmpeg进行视频拼接
     * 
     * @param videoFiles 视频文件路径列表
     * @param outputPath 输出文件路径
     * @return 总是返回false（已禁用）
     */
    @SuppressWarnings("unused")
    private boolean tryJCodecConcat(List<String> videoFiles, String outputPath) {
        logger.error("JCodec视频拼接已被禁用，因为会导致内存溢出。请使用FFmpeg进行视频拼接");
        return false;
    }

    /**
     * 下载并拼接多个视频
     * @param videoUrls 视频URL列表
     * @param outputPath 输出文件路径
     * @return 是否成功
     */
    public boolean downloadAndConcatVideos(List<String> videoUrls, String outputPath) {
        if (videoUrls == null || videoUrls.isEmpty()) {
            logger.error("视频URL列表为空，无法下载和拼接");
            return false;
        }

        try {
            // 创建临时目录
            Path tempPath = Paths.get(tempDir);
            if (!Files.exists(tempPath)) {
                Files.createDirectories(tempPath);
            }

            // 下载所有视频
            List<String> downloadedFiles = new java.util.ArrayList<>();
            for (int i = 0; i < videoUrls.size(); i++) {
                String videoUrl = videoUrls.get(i);
                String tempFile = tempDir + File.separator + "video_" + UUID.randomUUID().toString() + ".mp4";
                
                if (downloadVideo(videoUrl, tempFile)) {
                    downloadedFiles.add(tempFile);
                    logger.info("视频 {} 下载成功: {}", i + 1, tempFile);
                } else {
                    logger.error("视频 {} 下载失败: {}", i + 1, videoUrl);
                    // 清理已下载的文件
                    for (String file : downloadedFiles) {
                        try {
                            Files.deleteIfExists(Paths.get(file));
                        } catch (Exception e) {
                            logger.warn("清理临时文件失败: {}", file, e);
                        }
                    }
                    return false;
                }
            }

            // 拼接视频
            boolean success = concatVideos(downloadedFiles, outputPath);

            // 清理临时文件
            for (String file : downloadedFiles) {
                try {
                    Files.deleteIfExists(Paths.get(file));
                } catch (Exception e) {
                    logger.warn("清理临时文件失败: {}", file, e);
                }
            }

            return success;
        } catch (Exception e) {
            logger.error("下载并拼接视频失败", e);
            return false;
        }
    }
}

