package com.example.sjkbf.controller;

import com.example.sjkbf.entity.projectconfig.Project;
import com.example.sjkbf.service.ProjectPerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.net.URLDecoder;
import java.nio.file.*;
import java.text.DecimalFormat;
import java.util.concurrent.*;

@RestController
@RequestMapping("/api")
public class VideoController {

    private static final String VIDEO_ROOT = "/root/rindro/Script/Project";
    private static final String TRANSCODE_ROOT = "/tmp/video-transcoded";

    private final ExecutorService transcodeExecutor = Executors.newFixedThreadPool(4);

    @Autowired
    private ProjectPerService projectPerService;
    private final DecimalFormat durationFormat = new DecimalFormat("0.00");

    @GetMapping("/videos/{project}/{videoName}")
    public ResponseEntity<Resource> streamVideo(
            @PathVariable String project,
            @PathVariable String videoName,
            @RequestHeader(value = "User-Agent", required = false) String userAgent,
            @RequestHeader(value = "Range", required = false) String rangeHeader) throws Exception {

        System.out.println("原始参数 - project: " + project + ", videoName: " + videoName);

//        project ="project";
//        videoName = "0";
        // 直接使用 Spring 已经解码的参数
        project = projectPerService.getByName(project).getImgPath();

//        project = projectPerService.getByName(decodedPath).getImgPath();

        Path sourcePath = Paths.get(VIDEO_ROOT, project, "vid", videoName + ".mp4");

        // 检查源文件是否存在
        if (!Files.exists(sourcePath)) {
            System.out.println("源文件不存在: " + sourcePath);
            return ResponseEntity.notFound().build();
        }

        boolean needsTranscode = needsTranscode(sourcePath, userAgent);

        if (needsTranscode) {
            Path transcodedPath = getTranscodedPath(project, videoName);

            // 如果转码文件不存在或正在转码，先返回原始文件
            if (!Files.exists(transcodedPath) || isTranscodingInProgress(transcodedPath)) {
                // 异步触发转码（如果还没开始的话）
                if (!Files.exists(transcodedPath)) {
                    startTranscode(project, videoName);
                }
                // 返回原始文件作为降级方案
                return buildVideoResponse(sourcePath, rangeHeader, false);
            }

            return buildVideoResponse(transcodedPath, rangeHeader, true);
        } else {
            return buildVideoResponse(sourcePath, rangeHeader, false);
        }
    }

    /**
     * 改进的转码检测逻辑
     */
    private boolean needsTranscode(Path videoPath, String userAgent) throws IOException {
        if (!Files.exists(videoPath)) {
            throw new FileNotFoundException("视频文件不存在: " + videoPath);
        }

        // 不再强制为移动设备转码，而是基于实际编码格式判断
        try {
            // 检测视频编码
            String videoCodec = getStreamCodec(videoPath, "v");
            String audioCodec = getStreamCodec(videoPath, "a");

            // 需要转码的情况：
            // 1. 视频不是H.264
            // 2. 音频不是AAC或MP3
            boolean needTranscode = !"h264".equalsIgnoreCase(videoCodec) ||
                    (!"aac".equalsIgnoreCase(audioCodec) && !"mp3".equalsIgnoreCase(audioCodec));

            System.out.println("视频编码: " + videoCodec + ", 音频编码: " + audioCodec + ", 需要转码: " + needTranscode);
            return needTranscode;

        } catch (Exception e) {
            // 如果检测失败，默认需要转码
            System.err.println("编码检测失败，默认需要转码: " + e.getMessage());
            return true;
        }
    }

    /**
     * 获取音视频流编码
     */
    private String getStreamCodec(Path videoPath, String streamType) throws IOException {
        Process proc = new ProcessBuilder(
                "ffprobe",
                "-v", "error",
                "-select_streams", streamType + ":0",
                "-show_entries", "stream=codec_name",
                "-of", "default=noprint_wrappers=1:nokey=1",
                videoPath.toString()
        ).start();

        try (BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
            String codec = br.readLine();
            return codec != null ? codec.trim() : "unknown";
        }
    }

    /**
     * 获取转码路径并触发转码
     */
    private Path getTranscodedPath(String project, String videoName) {
        return Paths.get(TRANSCODE_ROOT, project, videoName + "_h264.mp4");
    }

    /**
     * 启动转码任务
     */
    private void startTranscode(String project, String videoName) {
        transcodeExecutor.submit(() -> transcodeVideo(project, videoName));
    }

    /**
     * 改进的转码方法 - 使用更兼容的参数
     */
    private void transcodeVideo(String project, String videoName) {
        Path transcodedPath = getTranscodedPath(project, videoName);
        Path tempPath = Paths.get(transcodedPath.toString() + ".tmp");

        try {
            // 确保目录存在
            Files.createDirectories(transcodedPath.getParent());

            String source = Paths.get(VIDEO_ROOT, project, "vid", videoName + ".mp4").toString();

            // 简化转码命令 - 提高兼容性
            String[] cmd = {
                    "ffmpeg", "-i", source,
                    "-c:v", "libx264",
                    "-preset", "medium",
                    "-crf", "23",
                    "-profile:v", "high",  // 改为high提高质量
                    "-level", "4.0",
                    "-c:a", "aac",
                    "-b:a", "128k",
                    "-movflags", "+faststart",  // 只保留faststart
                    "-y", tempPath.toString()   // 先输出到临时文件
            };

            System.out.println("执行转码命令: " + String.join(" ", cmd));

            ProcessBuilder pb = new ProcessBuilder(cmd);
            pb.redirectErrorStream(true);
            Process p = pb.start();

            // 读取输出用于调试
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println("FFmpeg: " + line);
                }
            }

            int exitCode = p.waitFor();

            if (exitCode == 0) {
                // 转码成功，重命名临时文件
                Files.move(tempPath, transcodedPath, StandardCopyOption.REPLACE_EXISTING);
                System.out.println("转码完成: " + transcodedPath);
            } else {
                System.err.println("转码失败，退出码: " + exitCode);
                Files.deleteIfExists(tempPath);
            }

        } catch (Exception e) {
            System.err.println("转码过程出错: " + e.getMessage());
            e.printStackTrace();
            try {
                Files.deleteIfExists(tempPath);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    /**
     * 检查是否正在转码
     */
    private boolean isTranscodingInProgress(Path transcodedPath) {
        Path tempPath = Paths.get(transcodedPath.toString() + ".tmp");
        return Files.exists(tempPath);
    }

    /**
     * 检查原始文件是否比转码文件新
     */
    private boolean isSourceNewer(String project, String videoName, Path transcodedPath) throws IOException {
        Path sourcePath = Paths.get(VIDEO_ROOT, project, "vid", videoName + ".mp4");
        return Files.getLastModifiedTime(sourcePath).toInstant()
                .isAfter(Files.getLastModifiedTime(transcodedPath).toInstant());
    }

    /**
     * 构建视频响应
     */
    private ResponseEntity<Resource> buildVideoResponse(Path videoPath, String rangeHeader, boolean isTranscoded)
            throws IOException {

        if (!Files.exists(videoPath)) {
            return ResponseEntity.notFound().build();
        }

        Resource resource = new FileSystemResource(videoPath);
        long fileLength = resource.contentLength();

        HttpHeaders headers = new HttpHeaders();
        headers.set("Accept-Ranges", "bytes");
        headers.set("Content-Disposition", "inline");

        // 处理范围请求
        if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
            String range = rangeHeader.substring(6);
            String[] ranges = range.split("-");

            long start = Long.parseLong(ranges[0]);
            long end = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileLength - 1;
            long rangeLength = end - start + 1;

            if (rangeLength < 0 || start < 0 || end >= fileLength) {
                return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
                        .header("Content-Range", "bytes */" + fileLength)
                        .build();
            }

            headers.set("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
            headers.setContentLength(rangeLength);

            // 读取部分内容
            InputStreamResource inputStreamResource = createPartialContent(resource, start, rangeLength);

            return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
                    .headers(headers)
                    .contentType(MediaType.parseMediaType("video/mp4"))
                    .body(inputStreamResource);
        }

        // 完整文件响应
        headers.setContentLength(fileLength);
        headers.set("Content-Type", "video/mp4");
        headers.set("X-Content-Duration", getVideoDuration(videoPath));
        headers.set("Cache-Control", isTranscoded ? "public, max-age=2592000" : "no-cache");

        return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
    }

    /**
     * 创建部分内容资源
     */
    private InputStreamResource createPartialContent(Resource resource, long start, long length) throws IOException {
        InputStream inputStream = resource.getInputStream();
        inputStream.skip(start);

        return new InputStreamResource(inputStream) {
            @Override
            public InputStream getInputStream() throws IOException {
                return new LimitedInputStream(inputStream, length);
            }

            @Override
            public long contentLength() throws IOException {
                return length;
            }
        };
    }

    /**
     * 限制输入流
     */
    private static class LimitedInputStream extends InputStream {
        private final InputStream wrapped;
        private long remaining;

        public LimitedInputStream(InputStream wrapped, long limit) {
            this.wrapped = wrapped;
            this.remaining = limit;
        }

        @Override
        public int read() throws IOException {
            if (remaining <= 0) return -1;
            int result = wrapped.read();
            if (result != -1) remaining--;
            return result;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            if (remaining <= 0) return -1;
            int toRead = (int) Math.min(len, remaining);
            int result = wrapped.read(b, off, toRead);
            if (result != -1) remaining -= result;
            return result;
        }
    }

    /**
     * 获取视频时长
     */
    private String getVideoDuration(Path videoPath) throws IOException {
        try {
            Process proc = new ProcessBuilder(
                    "ffprobe", "-v", "error",
                    "-show_entries", "format=duration",
                    "-of", "default=noprint_wrappers=1:nokey=1",
                    videoPath.toString()
            ).start();

            try (BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
                String duration = br.readLine();
                if (duration != null && !duration.trim().isEmpty()) {
                    return durationFormat.format(Double.parseDouble(duration.trim()));
                }
            }
        } catch (Exception e) {
            System.err.println("获取视频时长失败: " + e.getMessage());
        }
        return "0.00";
    }
}