package com.yfd.platform.component.voiceserver.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.LogFactory;
import com.yfd.platform.component.mqtt.MqttGateway;
import com.yfd.platform.component.voiceserver.DataGroup;
import com.yfd.platform.component.voiceserver.VoicePacketHandler;
import com.yfd.platform.component.voiceserver.VoiceServerService;
import com.yfd.platform.config.HttpServerConfig;
import com.yfd.platform.config.thread.ThreadPoolExecutorUtil;
import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice;
import com.yfd.platform.modules.basedata.domain.VoicePatrolLog;
import com.yfd.platform.modules.basedata.mapper.VoicePatrolLogMapper;
import com.yfd.platform.utils.ExecutionJob;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Date: 2025/3/6 18:28
 * @Description:
 */
@Service
public class VoiceServerServiceImpl implements VoiceServerService {

    @Autowired
    private MqttGateway mqttGateway;
    @Resource
    private HttpServerConfig httpServerConfig;
    @Resource
    private VoicePatrolLogMapper voicePatrolLogMapper;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean sendVoiceServerControl(String ID, String actionPower, String fileName) {
        if ("ON".equals(actionPower)) {
            VoicePacketHandler.dataGroups.clear();
        }
        JSONObject data = new JSONObject();
        data.putOpt("GLOBAL_MODE", "ACTION");
        data.putOpt("GLOBAL_CLIENTID", ID);
        data.putOpt("GLOBAL_GROUPID", "admin");
        data.putOpt("ACTION_POWER", actionPower);
        try {
            mqttGateway.sendToMqtt("CONTROL/" + ID, 1, JSONUtil.toJsonStr(data));
        } catch (Exception e) {
            e.printStackTrace();
        }
        if ("OFF".equals(actionPower)) {
            if ("3".equals(httpServerConfig.getFilefromtype())) {
                FileUtil.copy("D:\\riis\\video\\sound.wav", httpServerConfig.getSnapFilePath() + fileName, true);
            }
            if ("1".equals(httpServerConfig.getFilefromtype())) {
                this.createAudioFile(fileName);
            }

        }
        return true;
    }

    @Override
    public boolean createAudioFile(String fileName) {
        List<DataGroup> voicePackets = VoicePacketHandler.dataGroups;
        if (voicePackets.size() <= 0) {
            return false;
        }
        DataGroup dataGroup = voicePackets.get(0);
        byte sampleSizeInBits = dataGroup.getBits();
        byte numChannels = dataGroup.getChannel();
        short sampleRate = dataGroup.getSampleRate();
        List<byte[]> pcmDataList = voicePackets.stream().map(DataGroup::getData).collect(Collectors.toList());
        try {
            FileUtil.touch(httpServerConfig.getSnapFilePath() + fileName);
            generateWavFile(httpServerConfig.getSnapFilePath() + fileName, pcmDataList, sampleRate, numChannels,
                    sampleSizeInBits);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    /***********************************
    * 用途说明: 线程执行声纹任务
    * 参数说明 device 设备
    * 参数说明 duration 录制时间
    * 参数说明 log 声音日志
    * 返回值说明: void
    ***********************************/
    @Override
    public void executeVoiceTaskAsync(SubstationPatroldevice device, String duration, VoicePatrolLog log) {
        try {
            ExecutionJob.EXECUTOR.submit(() -> {
                // 生成文件路径（异步线程内操作）
                String fullPath = buildFilePath(device);
                log.setFileName(fullPath);
                log.setFilePath(httpServerConfig.getSnapFilePath() + fullPath);

                // 控制音频采集
                this.sendVoiceServerControl(
                        device.getPatroldeviceCode(), "ON", fullPath
                );
                try {
                    TimeUnit.SECONDS.sleep(Integer.parseInt(duration));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.sendVoiceServerControl(
                        device.getPatroldeviceCode(), "OFF", fullPath
                );
                // 保存日志
                voicePatrolLogMapper.insert(log);
                redisTemplate.delete(device.getPatroldeviceId());
            });
        } catch (Exception e) {
            // 异常处理：记录详细日志
            e.printStackTrace();
        }
    }

    private String buildFilePath(SubstationPatroldevice device) {
        return String.format("%s/%s/%s/%s/%s/%s/%s-%s.wav",
                device.getStationCode(),
                DateUtil.format(new Date(), "yyyy"),
                DateUtil.format(new Date(), "MM"),
                DateUtil.format(new Date(), "dd"),
                device.getPatroldeviceCode(),
                "Audio",
                device.getPatroldeviceCode(),
                DateUtil.format(new Date(), "yyyyMMddHHmmss")
        );
    }

    public byte[] mergePcmData(List<byte[]> pcmDataList) {
        int totalLength = pcmDataList.stream().mapToInt(data -> data.length).sum();
        byte[] fullPcmData = new byte[totalLength];
        int offset = 0;
        for (byte[] data : pcmDataList) {
            System.arraycopy(data, 0, fullPcmData, offset, data.length);
            offset += data.length;
        }
        return fullPcmData;
    }

    public void generateWavFile(String savePath, List<byte[]> pcmDataList, int sampleRate, int numChannels,
                                int bitsPerSample) throws IOException {
        // 合并 PCM 数据
        byte[] fullPcmData = mergePcmData(pcmDataList);

        // 构建 WAV 文件头
        byte[] header = buildWavHeader(sampleRate, numChannels, bitsPerSample, fullPcmData.length);

        // 写入文件
        try (FileOutputStream fos = new FileOutputStream(savePath)) {
            fos.write(header);
            fos.write(fullPcmData);
        }
        System.out.println("音频数据已保存为 " + savePath);
    }

    private byte[] buildWavHeader(int sampleRate, int numChannels, int bitsPerSample, int dataSize) throws IOException {
        ByteArrayOutputStream headerStream = new ByteArrayOutputStream();

        // ChunkID: "RIFF" (4 bytes)
        headerStream.write("RIFF".getBytes());

        // ChunkSize: File size - 8 (4 bytes, little-endian)
        int fileSize = 36 + dataSize; // 36 = 44 - 8 (header size)
        headerStream.write(ByteBuffer.allocate(4)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(fileSize)
                .array());

        // Format: "WAVE" (4 bytes)
        headerStream.write("WAVE".getBytes());

        // Subchunk1ID: "fmt " (4 bytes)
        headerStream.write("fmt ".getBytes());

        // Subchunk1Size: 16 (4 bytes, little-endian)
        headerStream.write(ByteBuffer.allocate(4)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(16)
                .array());

        // AudioFormat: 1 (PCM, 2 bytes, little-endian)
        headerStream.write(ByteBuffer.allocate(2)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putShort((short) 1)
                .array());

        // NumChannels (2 bytes, little-endian)
        headerStream.write(ByteBuffer.allocate(2)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putShort((short) numChannels)
                .array());

        // SampleRate (4 bytes, little-endian)
        headerStream.write(ByteBuffer.allocate(4)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(sampleRate)
                .array());

        // ByteRate: SampleRate * NumChannels * BitsPerSample/8 (4 bytes, little-endian)
        int byteRate = sampleRate * numChannels * (bitsPerSample / 8);
        headerStream.write(ByteBuffer.allocate(4)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(byteRate)
                .array());

        // BlockAlign: NumChannels * BitsPerSample/8 (2 bytes, little-endian)
        short blockAlign = (short) (numChannels * (bitsPerSample / 8));
        headerStream.write(ByteBuffer.allocate(2)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putShort(blockAlign)
                .array());

        // BitsPerSample (2 bytes, little-endian)
        headerStream.write(ByteBuffer.allocate(2)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putShort((short) bitsPerSample)
                .array());

        // Subchunk2ID: "data" (4 bytes)
        headerStream.write("data".getBytes());

        // Subchunk2Size: data size (4 bytes, little-endian)
        headerStream.write(ByteBuffer.allocate(4)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(dataSize)
                .array());

        return headerStream.toByteArray();
    }

}
