package com.fs.app.util;

import com.fs.common.utils.uuid.IdUtils;
import com.fs.system.oss.CloudStorageService;
import com.fs.system.oss.OSSFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.HttpStatus;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public class AudioUtils {

    /**
     * silk转换为mp3
     * @param silkUrl silk语音链接地址
     * @return  mp3链接地址
     */
    public static String convertSilk2Mp3(String silkUrl) {
        String uniqueId = IdUtils.fastSimpleUUID();
        Path uploadDirPath = Paths.get(System.getProperty("java.io.tmpdir"), "/");
        Path downloadedSilkFilePath = uploadDirPath.resolve(uniqueId + ".silk");
        Path pcmFilePath = uploadDirPath.resolve(uniqueId + ".pcm");
        Path mp3FilePath = uploadDirPath.resolve(uniqueId + ".mp3");

        try {
            // 1. 从网络下载 SILK 文件
            downloadFile(silkUrl, downloadedSilkFilePath);
            log.info("SILK file downloaded to: {}", downloadedSilkFilePath);

            // 2. 使用 silk-v3-decoder 解码 SILK 到 PCM
            List<String> silkDecodeCommand = new ArrayList<>();
            silkDecodeCommand.add("silk_v3_decoder");
            silkDecodeCommand.add(downloadedSilkFilePath.toString());
            silkDecodeCommand.add(pcmFilePath.toString());

            ProcessBuilder silkDecoderPb = new ProcessBuilder(silkDecodeCommand);
            silkDecoderPb.redirectErrorStream(true); // 将错误流合并到标准输出
            Process silkDecoderProcess = silkDecoderPb.start();

            String silkDecoderOutput = readInputStreamToString(silkDecoderProcess.getInputStream());

            boolean silkDecoderExited = silkDecoderProcess.waitFor(60, TimeUnit.SECONDS);
            if (!silkDecoderExited || silkDecoderProcess.exitValue() != 0) {
                log.error("silk conversion failed or timed out. error: {}", silkDecoderOutput);
                return null;
            }
            log.info("SILK decoder to PCM successfully.");

            // 3. 使用 FFmpeg 将 PCM 转码为 MP3
            Process ffmpegProcess = getFfmpegProcess(pcmFilePath, mp3FilePath);
            String ffmpegOutput = readInputStreamToString(ffmpegProcess.getInputStream());

            boolean ffmpegExited = ffmpegProcess.waitFor(120, TimeUnit.SECONDS);
            if (!ffmpegExited || ffmpegProcess.exitValue() != 0) {
                log.error("ffmpeg conversion failed or timed out. error: {}", ffmpegOutput);
                return null;
            }
            log.info("ffmpeg conversion to MP3 successfully.");

            // 4. 上传oss
            String fileName = mp3FilePath.getFileName().toString();
            String suffix = fileName.substring(fileName.lastIndexOf("."));
            CloudStorageService storage = OSSFactory.build();
            return storage.uploadSuffix(Files.newInputStream(mp3FilePath), suffix);

        } catch (IOException | InterruptedException | NullPointerException e) {
            log.error("Conversion error: {}", e.getMessage());
            return null;
        } finally {
            // 清理临时文件 (重要!)
            try {
                if (Files.exists(downloadedSilkFilePath)) Files.delete(downloadedSilkFilePath);
                if (Files.exists(pcmFilePath)) Files.delete(pcmFilePath);
                if (Files.exists(mp3FilePath)) Files.delete(mp3FilePath);
            } catch (IOException e) {
                log.error("Error cleaning up temporary files:: {}", e.getMessage());
            }
        }
    }

    /**
     * 执行ffmpeg
     * @param pcmFilePath   pcm文件
     * @param mp3FilePath   mp3地址
     * @return  process
     * @throws IOException exception
     */
    private static Process getFfmpegProcess(Path pcmFilePath, Path mp3FilePath) throws IOException {
        List<String> ffmpegCommand = new ArrayList<>();
        ffmpegCommand.add("ffmpeg");
        ffmpegCommand.add("-y");
        ffmpegCommand.add("-f");
        ffmpegCommand.add("s16le");
        ffmpegCommand.add("-ar");
        ffmpegCommand.add("24000"); // 注意：这里假设是 24kHz，如果你的 SILK 文件是其他采样率，请调整
        ffmpegCommand.add("-ac");
        ffmpegCommand.add("1");
        ffmpegCommand.add("-i");
        ffmpegCommand.add(pcmFilePath.toString());
        ffmpegCommand.add(mp3FilePath.toString());

        ProcessBuilder ffmpegPb = new ProcessBuilder(ffmpegCommand);
        ffmpegPb.redirectErrorStream(true);
        return ffmpegPb.start();
    }

    /**
     * 处理文件流
     * @param is 输入流
     * @return  输出
     * @throws IOException exception
     */
    private static String readInputStreamToString(InputStream is) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        return bos.toString("UTF-8"); // 使用 UTF-8 编码
    }

    /**
     * 下载网络文件
     * @param fileUrl       网络文件
     * @param destination   临时文件
     * @throws IOException  exception
     */
    private static void downloadFile(String fileUrl, Path destination) throws IOException {
        try (CloseableHttpClient httpClient = HttpClients.createDefault();
             CloseableHttpResponse response = httpClient.execute(new HttpGet(fileUrl));
             InputStream inputStream = response.getEntity().getContent();
             FileOutputStream outputStream = new FileOutputStream(destination.toFile())) {

            if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
                throw new IOException("Failed to download file from " + fileUrl + ", HTTP Status: " + response.getStatusLine().getStatusCode());
            }

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
    }
}
