package com.fuyuanshen.equipment.utils;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 使用 FFmpeg 的音频转码工具
 */
@Slf4j
@Component
public class AudioProcessUtil {

    // 目标音频参数
    private static final int TARGET_SAMPLE_RATE = 16000;
    private static final int TARGET_CHANNELS = 1;
    private static final int TARGET_BIT_RATE = 256000;

    // 最大输出大小：1MB
    private static final int MAX_OUTPUT_SIZE = 1024 * 1024;
    private static final int WAV_HEADER_SIZE = 44;

    // 16进制字符数组
    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

    /**
     * 使用 FFmpeg 将音频文件转码为标准 PCM-WAV
     */
    public byte[] uploadToStandardPcm(File file) throws IOException {
        if (file == null || !file.exists()) {
            throw new IOException("音频文件不存在");
        }

        FFmpegFrameGrabber grabber = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        try {
            grabber = new FFmpegFrameGrabber(file);
            grabber.setSampleRate(TARGET_SAMPLE_RATE);
            grabber.setAudioChannels(TARGET_CHANNELS);
            grabber.start();

            // 创建内存录制器
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
                    outputStream,
                    grabber.getAudioChannels()
            );

            recorder.setSampleRate(TARGET_SAMPLE_RATE);
            recorder.setAudioChannels(TARGET_CHANNELS);
            recorder.setAudioBitrate(TARGET_BIT_RATE);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);
            recorder.setFormat("wav");
            recorder.start();

            Frame frame;
            int frameCount = 0;
            int maxFrames = 1000; // 防止无限循环的安全限制

            while (frameCount < maxFrames &&
                    outputStream.size() < MAX_OUTPUT_SIZE &&
                    (frame = grabber.grab()) != null) {

                if (frame.samples != null) {
                    recorder.record(frame);
                    frameCount++;
                }
            }

            recorder.stop();
            recorder.release();

            byte[] wavData = outputStream.toByteArray();
            log.info("音频转码成功，输入文件: {}, 输出大小: {} bytes",
                    file.getName(), wavData.length);

            return wavData;

        } catch (Exception e) {
            log.error("音频转码失败", e);
            throw new IOException("音频转码失败: " + e.getMessage(), e);
        } finally {
            try {
                if (grabber != null) {
                    grabber.stop();
                    grabber.release();
                }
            } catch (Exception e) {
                log.warn("关闭音频抓取器失败", e);
            }
        }
    }

    /**
     * 按时间截取音频（保证内容完整性）
     */
    public byte[] uploadToStandardPcmWithFixedDuration(File file, int durationSeconds) throws IOException {
        if (file == null || !file.exists()) {
            throw new IOException("音频文件不存在");
        }

        FFmpegFrameGrabber grabber = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        try {
            grabber = new FFmpegFrameGrabber(file);
            grabber.setSampleRate(TARGET_SAMPLE_RATE);
            grabber.setAudioChannels(TARGET_CHANNELS);
            grabber.start();

            // 计算目标样本数
            int targetSamples = TARGET_SAMPLE_RATE * durationSeconds;

            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
                    outputStream,
                    grabber.getAudioChannels()
            );

            recorder.setSampleRate(TARGET_SAMPLE_RATE);
            recorder.setAudioChannels(TARGET_CHANNELS);
            recorder.setAudioBitrate(TARGET_BIT_RATE);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);
            recorder.setFormat("wav");
            recorder.start();

            Frame frame;
            int samplesCollected = 0;

            while (samplesCollected < targetSamples && (frame = grabber.grab()) != null) {
                if (frame.samples != null) {
                    // 计算这一帧的样本数
                    int samplesInFrame = frame.samples[0].remaining() / 2; // 16bit = 2 bytes per sample

                    // 如果这一帧会超过目标，创建部分帧
                    if (samplesCollected + samplesInFrame > targetSamples) {
                        int samplesNeeded = targetSamples - samplesCollected;
                        frame = createPartialFrame(frame, samplesNeeded);
                    }

                    recorder.record(frame);
                    samplesCollected += samplesInFrame;
                }
            }

            // 如果音频太短，用静音填充剩余部分
            if (samplesCollected < targetSamples) {
                int silenceSamples = targetSamples - samplesCollected;
                Frame silenceFrame = createSilenceFrame(silenceSamples);
                recorder.record(silenceFrame);
            }

            recorder.stop();
            recorder.release();

            byte[] wavData = outputStream.toByteArray();
            log.info("音频按时间截取成功，目标时长: {}秒, 实际输出: {} bytes",
                    durationSeconds, wavData.length);

            return wavData;

        } catch (Exception e) {
            log.error("音频处理失败", e);
            throw new IOException("音频处理失败: " + e.getMessage(), e);
        } finally {
            // 资源清理...
        }
    }

    /**
     * 创建部分帧（用于精确控制时长）
     */
    private Frame createPartialFrame(Frame originalFrame, int samplesNeeded) {
        Frame partialFrame = new Frame();
        partialFrame.sampleRate = originalFrame.sampleRate;
        partialFrame.audioChannels = originalFrame.audioChannels;

        ByteBuffer originalBuffer = (ByteBuffer) originalFrame.samples[0];
        ByteBuffer partialBuffer = ByteBuffer.allocate(samplesNeeded * 2); // 16bit

        // 复制需要的样本数
        for (int i = 0; i < samplesNeeded * 2 && originalBuffer.hasRemaining(); i++) {
            partialBuffer.put(originalBuffer.get());
        }
        partialBuffer.flip();

        partialFrame.samples = new Buffer[]{partialBuffer};
        return partialFrame;
    }

    /**
     * 创建静音帧
     */
    private Frame createSilenceFrame(int samples) {
        Frame silenceFrame = new Frame();
        silenceFrame.sampleRate = TARGET_SAMPLE_RATE;
        silenceFrame.audioChannels = TARGET_CHANNELS;

        ByteBuffer silenceBuffer = ByteBuffer.allocate(samples * 2); // 16bit
        // 静音数据就是全0
        while (silenceBuffer.hasRemaining()) {
            silenceBuffer.put((byte) 0);
        }
        silenceBuffer.flip();

        silenceFrame.samples = new Buffer[]{silenceBuffer};
        return silenceFrame;
    }

    /**
     * 将 WAV 字节数据保存为文件（用于测试验证）
     * @param wavData 完整的 WAV 文件数据（包含头部）
     * @param filename 保存的文件名，如 "test_audio.wav"
     * @return 保存的文件路径
     */
    public String saveWavToFile(byte[] wavData, String filename) {
        if (wavData == null || wavData.length == 0) {
            log.warn("无法保存空的 WAV 数据");
            return null;
        }

        // 创建测试目录
        File testDir = new File("audio_test_output");
        if (!testDir.exists()) {
            testDir.mkdirs();
        }

        try {
            // 生成带时间戳的文件名
            String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String safeFilename = filename.replaceAll("[^a-zA-Z0-9.-]", "_");
            String fullFilename = timestamp + "_" + safeFilename;

            File outputFile = new File(testDir, fullFilename);

            // 写入文件
            try (FileOutputStream fos = new FileOutputStream(outputFile)) {
                fos.write(wavData);
            }

            String absolutePath = outputFile.getAbsolutePath();
            log.info("WAV 文件保存成功: {}, 文件大小: {} bytes", absolutePath, wavData.length);

            // 验证文件有效性
            if (validateWavFile(outputFile)) {
                log.info("WAV 文件验证成功: {}", absolutePath);
            } else {
                log.warn("WAV 文件验证失败，文件可能损坏: {}", absolutePath);
            }

            return absolutePath;

        } catch (Exception e) {
            log.error("保存 WAV 文件失败", e);
            return null;
        }
    }

    /**
     * 验证 WAV 文件的有效性
     */
    private boolean validateWavFile(File wavFile) {
        try {
            if (!wavFile.exists() || wavFile.length() < WAV_HEADER_SIZE) {
                return false;
            }

            byte[] fileData = Files.readAllBytes(wavFile.toPath());

            // 检查 RIFF 头
            if (fileData[0] != 'R' || fileData[1] != 'I' || fileData[2] != 'F' || fileData[3] != 'F') {
                return false;
            }

            // 检查 WAVE 标识
            if (fileData[8] != 'W' || fileData[9] != 'A' || fileData[10] != 'V' || fileData[11] != 'E') {
                return false;
            }

            // 检查文件大小是否匹配
            int statedSize = ByteBuffer.wrap(fileData, 4, 4)
                    .order(ByteOrder.LITTLE_ENDIAN)
                    .getInt();
            int actualSize = fileData.length - 8; // 减去 "RIFF" 和大小字段

            return statedSize == actualSize;

        } catch (Exception e) {
            log.warn("WAV 文件验证异常", e);
            return false;
        }
    }

    /**
     * 获取音频信息（用于调试）
     */
    public String getAudioInfo(byte[] wavData) {
        if (wavData == null || wavData.length < WAV_HEADER_SIZE) {
            return "无效的 WAV 数据";
        }

        try {
            ByteBuffer buffer = ByteBuffer.wrap(wavData).order(ByteOrder.LITTLE_ENDIAN);

            // 读取 WAV 头信息
            int audioFormat = buffer.getShort(20); // 音频格式
            int channels = buffer.getShort(22);    // 声道数
            int sampleRate = buffer.getInt(24);    // 采样率
            int byteRate = buffer.getInt(28);      // 字节率
            int blockAlign = buffer.getShort(32);  // 块对齐
            int bitsPerSample = buffer.getShort(34); // 位深
            int dataSize = buffer.getInt(40);      // 数据大小

            int durationMs = (dataSize * 1000) / byteRate; // 计算时长（毫秒）

            return String.format(
                    "格式: %s, 声道: %d, 采样率: %d Hz, 位深: %d bit, 时长: %.2f 秒, 数据大小: %d bytes",
                    audioFormat == 1 ? "PCM" : "未知(" + audioFormat + ")",
                    channels,
                    sampleRate,
                    bitsPerSample,
                    durationMs / 1000.0,
                    dataSize
            );

        } catch (Exception e) {
            return "解析音频信息失败: " + e.getMessage();
        }
    }

    /**
     * 备用的手动 WAV 文件生成方法
     */
    public byte[] convertToStandardWav(File file) throws IOException {
        try {
            // 使用 FFmpeg 读取音频数据
            byte[] pcmData = extractPcmData(file);

            // 生成 WAV 文件头
            byte[] wavHeader = createWavHeader(pcmData.length);

            // 合并头和数据
            byte[] wavData = new byte[wavHeader.length + pcmData.length];
            System.arraycopy(wavHeader, 0, wavData, 0, wavHeader.length);
            System.arraycopy(pcmData, 0, wavData, wavHeader.length, pcmData.length);

            return wavData;
        } catch (Exception e) {
            throw new IOException("音频转换失败: " + e.getMessage(), e);
        }
    }

    /**
     * 将原始 PCM 数据（无头）转换为标准 WAV 格式（带44字节头）
     * 输入：单声道、16bit、16000Hz 的 raw PCM
     * 输出：完整 WAV 文件字节数组
     */
    public byte[] rawPcmToStandardWav(byte[] rawPcmData) throws IOException {
        try {
        // 生成WAV头
        byte[] wavHeader = createWavHeader(rawPcmData.length);

        // 合并头 + 数据
        byte[] wavData = new byte[wavHeader.length + rawPcmData.length];
        System.arraycopy(wavHeader, 0, wavData, 0, wavHeader.length);
        System.arraycopy(rawPcmData, 0, wavData, wavHeader.length, rawPcmData.length);

        log.info("原始PCM转为标准WAV成功，原始大小: {} bytes, 输出大小: {} bytes",
                rawPcmData.length, wavData.length);

        return wavData;
        } catch (Exception e) {
            throw new IOException("音频转换失败: " + e.getMessage(), e);
        }
    }

    /**
     * 提取 PCM 数据
     */
    private byte[] extractPcmData(File file) throws Exception {
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(file);
        grabber.setSampleRate(TARGET_SAMPLE_RATE);
        grabber.setAudioChannels(TARGET_CHANNELS);
        grabber.start();

        ByteArrayOutputStream pcmStream = new ByteArrayOutputStream();
        Frame frame;
        int samplesProcessed = 0;
        int maxSamples = TARGET_SAMPLE_RATE * 60;   // 最多 60 秒

        try {
            while (samplesProcessed < maxSamples &&
                    pcmStream.size() < MAX_OUTPUT_SIZE &&
                    (frame = grabber.grab()) != null) {

                if (frame.samples != null && frame.samples[0] != null) {
                    ShortBuffer shortBuf = (ShortBuffer) frame.samples[0];
                    int remaining = shortBuf.remaining();
                    byte[] frameBytes = new byte[remaining * 2];
                    ByteBuffer.wrap(frameBytes)
                            .order(ByteOrder.LITTLE_ENDIAN)
                            .asShortBuffer()
                            .put(shortBuf);
                    pcmStream.write(frameBytes);
                    samplesProcessed += remaining;
                }
            }
        } finally {
            grabber.stop();
            grabber.release();
        }
        return pcmStream.toByteArray();
    }

    /**
     * 创建 WAV 文件头
     */
    private byte[] createWavHeader(int dataLength) {
        short channels = TARGET_CHANNELS;
        short sampleBits = 16; // 16-bit
        int sampleRate = TARGET_SAMPLE_RATE;
        short blockAlign = (short) (channels * sampleBits / 8);
        int byteRate = sampleRate * blockAlign;
        int riffLength = dataLength + WAV_HEADER_SIZE - 8;

        ByteBuffer buffer = ByteBuffer.allocate(WAV_HEADER_SIZE)
                .order(ByteOrder.LITTLE_ENDIAN);

        buffer.put("RIFF".getBytes());
        buffer.putInt(riffLength);
        buffer.put("WAVE".getBytes());
        buffer.put("fmt ".getBytes());
        buffer.putInt(16);
        buffer.putShort((short) 1); // PCM format
        buffer.putShort(channels);
        buffer.putInt(sampleRate);
        buffer.putInt(byteRate);
        buffer.putShort(blockAlign);
        buffer.putShort(sampleBits);
        buffer.put("data".getBytes());
        buffer.putInt(dataLength);

        return buffer.array();
    }

    /**
     * 获取支持的音频格式列表
     */
    public String[] getSupportedFormats() {
        return new String[] {
                "mp3", "wav", "aac", "flac", "m4a", "ogg", "wma",
                "amr", "aiff", "au", "mp2", "ac3", "opus"
        };
    }

    /**
     * 将字节数组转换为16进制字符串列表
     * 每个字节转换为两个16进制字符，作为一个字符串元素
     */
    public List<String> bytesToHexList(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return new ArrayList<>();
        }

        List<String> hexList = new ArrayList<>(bytes.length);
        for (byte b : bytes) {
            int value = b & 0xFF;
            char high = HEX_ARRAY[value >>> 4];
            char low = HEX_ARRAY[value & 0x0F];
            hexList.add(String.valueOf(high) + low);
        }

        log.debug("字节数组转换为16进制列表，字节数: {}, 16进制元素数: {}",
                bytes.length, hexList.size());
        return hexList;
    }

    /**
     * 将字节数组转换为十进制字符串列表
     * 每个字节转换为一个十进制字符串元素
     */
    public List<String> bytesToDecList(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return new ArrayList<>();
        }

        List<String> decList = new ArrayList<>(bytes.length);
        for (byte b : bytes) {
            int value = b & 0xFF; // 0-255
            decList.add(String.valueOf(value));
        }

        log.debug("字节数组转换为十进制列表，字节数: {}, 十进制元素数: {}", bytes.length, decList.size());
        return decList;
    }
}