package com.fuyuanshen.app.service;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fuyuanshen.app.domain.bo.AppDeviceBindRecordBo;
import com.fuyuanshen.app.domain.dto.AppAudioFileDto;
import com.fuyuanshen.app.domain.dto.AppFileRenameDto;
import com.fuyuanshen.app.domain.vo.AppDeviceBindRecordVo;
import com.fuyuanshen.app.http.HttpTtsClient;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import com.fuyuanshen.equipment.mapper.AppBusinessFileMapper;
import com.fuyuanshen.equipment.service.IAppBusinessFileService;
import com.fuyuanshen.equipment.utils.AlibabaTTSUtil;
import com.fuyuanshen.equipment.utils.AudioProcessUtil;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import com.fuyuanshen.equipment.utils.Mp3Duration;
import com.fuyuanshen.global.mqtt.utils.FfmpegVolumeUtil;
import com.fuyuanshen.global.mqtt.utils.FfmpegVolumeUtil3;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.service.ISysOssService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * 音频处理服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AudioProcessService {

    // 配置参数
    private static final int MAX_AUDIO_SIZE = 5 * 1024 * 1024; // 5MB
    private static final List<String> SUPPORTED_FORMATS = Arrays.asList(
            ".wav", ".mp3", ".aac", ".flac", ".m4a", ".ogg"
    );

    private final AudioProcessUtil audioProcessUtil;
    private final AlibabaTTSUtil alibabaTTSUtil;

    private final FileHashUtil fileHashUtil;
    private final ISysOssService ossService;
    private final IAppBusinessFileService appBusinessFileService;
    private final AppBusinessFileMapper appBusinessFileMapper;
    private final IAppDeviceBindRecordService appDeviceBindRecordService;

//    String accessKeyId = "LTAI5t66moCkhNC32TDJ5ReP";
//    String accessKeySecret = "2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH";
//    String appKey = "lbGuq5K5bEH4uxmT";
    @Value("${alibaba.tts.appKey}")
    private String appKey;
    @Value("${alibaba.tts.akId}")
    private String accessKeyId;
    @Value("${alibaba.tts.akSecret}")
    private String accessKeySecret;

    /**
     * 处理上传的音频文件
     */
    public List<String> processAudio(MultipartFile file) {
        // 1. 参数校验
        validateAudioFile(file);

        File tempFile = null;
        try {
            // 2. 创建临时文件
            tempFile = createTempAudioFile(file);

            // 3. 转码为标准PCM-WAV格式
            byte[] pcmData = audioProcessUtil.convertToStandardWav(tempFile);
            log.info("音频处理成功，输出数据大小: {} bytes", pcmData.length);

            // 获取音频信息
//            String audioInfo = audioProcessUtil.getAudioInfo(pcmData);
//            log.info("音频处理成功，音频信息: {}", audioInfo);
//
//            // 保存测试文件（用于验证）
//            String savedPath = audioProcessUtil.saveWavToFile(pcmData, "test_output.wav");
//            if (savedPath != null) {
//                log.info("测试文件已保存: {}", savedPath);
//            }

            // 将byte[]转换为16进制字符串列表
            List<String> hexList = audioProcessUtil.bytesToHexList(pcmData);

            log.info("音频处理完成，原始数据大小: {} bytes, 16进制数据长度: {}",
                    pcmData.length, hexList.size());

            return hexList;

        } catch (Exception e) {
            log.error("音频处理失败", e);
            throw new RuntimeException("音频处理失败", e);
        } finally {
            // 4. 清理临时文件
            deleteTempFile(tempFile);
        }
    }

    /**
     * 生成标准PCM数据（单声道，16K采样率，16bit深度，包含44字节WAV头）
     * 数据总大小不超过2MB，如果超过将抛出异常
     * @param text 要转换的文本内容
     * @return 标准PCM数据字节数组（WAV格式）
     * @throws IOException 处理失败时抛出
     * @throws IllegalArgumentException 如果生成的数据超过2MB
     */
    public List<String> generateStandardPcmData(String text) throws IOException {
        // 参数校验
        if (text == null || text.trim().isEmpty()) {
            throw new IllegalArgumentException("文本内容不能为空");
        }
        if (text.length() > 100) {
            throw new IllegalArgumentException("文本长度超过限制（最大100字符）");
        }
        log.info("输入文本长度: {}", text.length());
        try {
            byte[] rawPcmData = alibabaTTSUtil.generateStandardPcmData(text);

            // 使用AudioProcessUtil转换成带头44字节 PCM
            byte[] pcmData = audioProcessUtil.rawPcmToStandardWav(rawPcmData);

//            String savedPath = audioProcessUtil.saveWavToFile(pcmData, "test_output.wav");
//            if (savedPath != null) {
//                log.info("测试文件已保存: {}", savedPath);
//            }

            // 保存WAV文件到本地
            String savedPath = saveByteArrayToFile(pcmData, "tts_output.wav");
            if (savedPath != null) {
                log.info("WAV文件已保存: {}", savedPath);
            }

            // 将byte[]转换为16进制字符串列表
            List<String> hexList = audioProcessUtil.bytesToHexList(pcmData);

            log.info("generateStandardPcmData音频处理完成，原始数据大小: {} bytes, 16进制数据长度: {}",
                    pcmData.length, hexList.size());

            return hexList;
        } finally {
            // 4. 清理临时文件
        }
    }

    public String saveWavFileLocally(String text, String filename) throws IOException {
        // 参数校验
        if (text == null || text.trim().isEmpty()) {
            throw new IllegalArgumentException("文本内容不能为空");
        }

        if (filename == null || filename.trim().isEmpty()) {
            filename = "tts_output.wav"; // 默认文件名
        }

        try {
            // 生成PCM数据
            byte[] rawPcmData = alibabaTTSUtil.generateStandardPcmData(text);

            // 转换为标准WAV格式（添加44字节头部）
            byte[] wavData = audioProcessUtil.rawPcmToStandardWav(rawPcmData);

            // 保存到本地文件
            String filePath = saveByteArrayToFile(wavData, filename);

            log.info("WAV文件已保存: {}", filePath);
            return filePath;
        } catch (Exception e) {
            log.error("保存WAV文件失败: {}", e.getMessage(), e);
            throw new IOException("保存WAV文件失败", e);
        }
    }

    private String saveByteArrayToFile(byte[] data, String filename) throws IOException {
        // 确定保存路径（可以是临时目录或指定目录）
        String directory = System.getProperty("java.io.tmpdir");// 使用系统临时目录
        File dir = new File(directory);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 创建完整文件路径
        File file = new File(dir, filename);

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


        return file.getAbsolutePath();
    }

    private String saveByteArrayToFile(InputStream inputStream, String filename) throws IOException {
        // 确定保存路径（可以是临时目录或指定目录）
        String directory = System.getProperty("java.io.tmpdir"); // 使用系统临时目录
        File dir = new File(directory);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 创建完整文件路径
        File file = new File(dir, filename);

        // 从输入流读取数据并写入文件
        try (FileOutputStream fos = new FileOutputStream(file);
             InputStream is = inputStream) {
            byte[] buffer = new byte[8192]; // 8KB缓冲区
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
        return file.getAbsolutePath();
    }


    /**
     * 验证音频文件
     */
    private void validateAudioFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }

        if (!isAudioFile(file.getOriginalFilename())) {
            throw new IllegalArgumentException("只允许上传音频文件");
        }

        if (file.getSize() > MAX_AUDIO_SIZE) {
            throw new IllegalArgumentException("音频大小不能超过5MB");
        }
    }

    /**
     * 判断是否是支持的音频格式
     */
    private boolean isAudioFile(String filename) {
        if (filename == null || filename.lastIndexOf('.') == -1) {
            return false;
        }
        String ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
        return SUPPORTED_FORMATS.contains(ext);
    }

    /**
     * 创建临时音频文件
     */
    private File createTempAudioFile(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String extension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }

        File tempFile = File.createTempFile("audio-", extension);
        file.transferTo(tempFile);
        log.debug("创建临时音频文件: {}", tempFile.getAbsolutePath());
        return tempFile;
    }

    /**
     * 删除临时文件
     */
    private void deleteTempFile(File file) {
        if (file != null && file.exists()) {
            if (file.delete()) {
                log.debug("删除临时文件成功: {}", file.getAbsolutePath());
            } else {
                log.warn("无法删除临时文件: {}", file.getAbsolutePath());
            }
        }
    }

    /**
     * 提取文本
     */
    public String extract(MultipartFile file) throws Exception {
        String name = file.getOriginalFilename();
        if (name == null ||
                (!name.endsWith(".txt") && !name.endsWith(".docx"))) {
            throw new IllegalArgumentException("仅支持 .txt 或 .docx");
        }
        if (file.getSize() > MAX_AUDIO_SIZE) {
            throw new IllegalArgumentException("文件超过5MB");
        }

        String text;
        /* 全程流式，不落地磁盘，不一次性读字节数组 */
        try (InputStream in = file.getInputStream()) {
            if (name.endsWith(".txt")) {
                text = readTxt(in);
            } else {
                text = readDocx(in);
            }
        }
        return text;
    }

    /* ---------- txt：按行读，StringBuilder 复用 ---------- */
    private String readTxt(InputStream in) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder(4096);
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line).append('\n');
        }
        return sb.toString();
    }

    /* ---------- docx：ZipInputStream 只扫 document.xml ---------- */
    private String readDocx(InputStream in) throws IOException {
        ZipInputStream zin = new ZipInputStream(in);
        ZipEntry e;
        while ((e = zin.getNextEntry()) != null) {
            if ("word/document.xml".equals(e.getName())) {
                return staxExtract(zin);   // 流式读 XML
            }
        }
        return "";
    }

    /* ---------- StAX 流式提取 <w:t> ---------- */
    private String staxExtract(InputStream xml) throws IOException {
        XMLStreamReader r = null;
        StringBuilder sb = new StringBuilder(4096);
        try {
            //System.out.println(new String(xml.readAllBytes()));
            r = XMLInputFactory.newInstance().createXMLStreamReader(xml);
            while (r.hasNext()) {
                if (r.next() == XMLStreamConstants.START_ELEMENT &&
                        "t".equals(r.getLocalName())) {
                    String elementText = r.getElementText();
                    sb.append(elementText);
                }
            }
        } catch (XMLStreamException ex) {
            throw new IOException(ex);
        } finally {
            if (r != null) try { r.close(); } catch (XMLStreamException ignore) {}
        }
        return sb.toString();
    }


    public String uploadAudioToOss(AppAudioFileDto bo) {
        MultipartFile file = bo.getFile();
        // 校验文件格式和大小
        validateAudioFileForRestful(file);
        // 上传文件
        // SysOssVo upload = sysOssService.upload(file);
        String filename = file.getOriginalFilename();
        String savedPath = null;
        try {
            String fileHash = fileHashUtil.hash(file);
            SysOssVo upload = ossService.updateHash(file, fileHash);
            // 强制将HTTP替换为HTTPS
            if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
                upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
            }
            String fileSuffix = filename.substring(filename.lastIndexOf('.')).toLowerCase();
            AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo();
            appBusinessFileBo.setFileId(upload.getOssId());
            appBusinessFileBo.setBusinessId(bo.getDeviceId());
            appBusinessFileBo.setFileType(3L);
            Long userId = getUserId(bo.getSource(), bo.getDeviceId());
            appBusinessFileBo.setCreateBy(userId);
            savedPath = saveByteArrayToFile(file.getInputStream(), generateRandomFileName(fileSuffix));
            if (savedPath != null) {
                log.info("MP3文件已保存: {}", savedPath);
                Integer mp3Duration = Mp3Duration.getMp3Duration(savedPath);
                log.info("MP3文件时长: {} 秒", mp3Duration);
                appBusinessFileBo.setDuration(mp3Duration);
            }
            appBusinessFileService.insertByBo(appBusinessFileBo);
            if (upload != null) {
                return upload.getUrl();
            }
        } catch (Exception e){
            log.error("上传音频文件失败", e);
        }finally {
            log.info("删除临时文件: {}", savedPath);
            if(savedPath != null){
                deleteTempFile(new File(savedPath));
            }
        }

        return null;
    }

    private Long getUserId(String source,Long deviceId){
        if("app".equals( source)){
            return AppLoginHelper.getUserId();
        } else if ("web".equals( source)){
//            AppDeviceBindRecordBo appDeviceBindRecordBo = new AppDeviceBindRecordBo();
//            appDeviceBindRecordBo.setDeviceId(deviceId);
////            appDeviceBindRecordBo.setCommunicationMode(0);
//            List<AppDeviceBindRecordVo> appDeviceBindRecordVos = appDeviceBindRecordService.queryList(appDeviceBindRecordBo);
//            if (appDeviceBindRecordVos != null && !appDeviceBindRecordVos.isEmpty()){
//                userId = appDeviceBindRecordVos.get(0).getBindingUserId();
//            }
        }
        return 0L;
    }
    /**
     * 校验音频文件格式
     */
    private void validateAudioFileForRestful(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }

        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        List<String> SUPPORTED_FORMATS = Arrays.asList(
                ".wav", ".mp3", ".pcm"
        );

        String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
        // 检查文件扩展名
        if (!SUPPORTED_FORMATS.contains(ext)) {
            throw new IllegalArgumentException("只允许上传MP3、WAV、PCM格式的音频文件");
        }

        long fileSize = 256 * 1024;
        // 检查文件大小
        if (file.getSize() > fileSize) {
            throw new IllegalArgumentException("音频大小不能超过256kb");
        }
    }

    /**
     * 判断文件是否为支持的音频格式
     */
    private boolean isSupportedFormat(String filename) {
        if (filename == null || filename.lastIndexOf('.') == -1) {
            return false;
        }
        String ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
        return SUPPORTED_FORMATS.contains(ext);
    }

    public String textToSpeech(Long deviceId,String text, String fileSuffix,String source) {
        //支持PCM/WAV/MP3格式
        if (fileSuffix == null || fileSuffix.isEmpty()) {
            fileSuffix = "mp3";
        }
        fileSuffix = fileSuffix.toLowerCase();
        List<String> SUPPORTED_FORMATS = Arrays.asList(
                "wav", "mp3", "pcm"
        );
        boolean contains = SUPPORTED_FORMATS.contains(fileSuffix);
        if (!contains) {
            throw new IllegalArgumentException("不支持的音频格式");
        }
//        String accessKeyId = "LTAI5t66moCkhNC32TDJ5ReP";
//        String accessKeySecret = "2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH";
//        String appKey = "lbGuq5K5bEH4uxmT";
        String savedPath = null;
        String savedMp3VolumePath = null;
        try {
            // 使用HTTP方式调用
            HttpTtsClient httpClient = new HttpTtsClient(accessKeyId, accessKeySecret, appKey);
//
            byte[] mp3Data = httpClient.synthesizeTextToMp3(text,fileSuffix);
//            byte[] mp3Data = alibabaTTSUtil.synthesizeTextToMp3(text,fileSuffix);


            AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo();

            savedPath = saveByteArrayToFile(mp3Data, generateRandomFileName(fileSuffix));

            Integer mp3Duration = getMp3Duration2(savedPath);
            appBusinessFileBo.setDuration(mp3Duration);

            String directory = System.getProperty("java.io.tmpdir"); // 使用系统临时目录
            String fileName = generateRandomFileName(fileSuffix);
            savedMp3VolumePath = directory + "/" + fileName;
            log.info("保存MP3文件: {}", savedMp3VolumePath);
            FfmpegVolumeUtil.increaseMp3Volume(savedPath, savedMp3VolumePath, 8);

            File file = new File(savedMp3VolumePath);
            String fileHash = fileHashUtil.getFileHash(file,"SHA-256");
            SysOssVo upload = ossService.updateHash(file, fileHash);

            // 强制将HTTP替换为HTTPS
            if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
                upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
            }

            appBusinessFileBo.setFileId(upload.getOssId());
            appBusinessFileBo.setBusinessId(deviceId);
            appBusinessFileBo.setFileType(3L);
            Long userId = getUserId(source, deviceId);
            appBusinessFileBo.setCreateBy(userId);
            appBusinessFileService.insertByBo(appBusinessFileBo);
            if (upload != null) {
                return upload.getUrl();
            }
        } catch (Exception e){
            log.error("上传音频文件失败", e);
        } finally {
            log.info("删除savedPath临时文件: {}", savedPath);
            if(savedPath != null){
                deleteTempFile(new File(savedPath));
            }
            if(savedMp3VolumePath != null){
                log.info("删除savedMp3VolumePath临时文件: {}", savedMp3VolumePath);
                deleteTempFile(new File(savedMp3VolumePath));
            }
        }
        return null;
    }

    private Integer getMp3Duration2(String savedPath) {
        if (savedPath != null) {
            log.info("MP3文件已保存: {}", savedPath);
            Integer mp3Duration = Mp3Duration.getMp3Duration(savedPath);
            log.info("MP3文件时长: {} 秒", mp3Duration);
            return  mp3Duration;
        }
        return 0;
    }

    private static final Random random = new Random();
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    /**
     * 生成纯随机文件名（无原文件名依赖）
     */
    public static String generateRandomFileName(String extension) {
        String timestamp = LocalDateTime.now().format(formatter);
        int randomNum = random.nextInt(10000);
        String uuidPart = UUID.randomUUID().toString().substring(0, 8);

        if (!extension.startsWith(".")) {
            extension = "." + extension;
        }

        return timestamp+extension;
    }

    public List<AppFileVo> queryAudioFileList(Long deviceId,String source) {
        if(deviceId == null){
            return null;
        }

        Long userId = getUserId(source, deviceId);
        AppBusinessFileBo bo = new AppBusinessFileBo();
        bo.setBusinessId(deviceId);
        bo.setFileType(3L);
        if("web".equals(source)){
            bo.setCreateBy(null);
        }else{
            bo.setCreateBy(userId);
        }
        return appBusinessFileService.queryAppFileList(bo);
    }

    public R<Void> deleteAudioFile(Long id) {
        UpdateWrapper<AppBusinessFile> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id",id);
        appBusinessFileMapper.delete(updateWrapper);
        return R.ok();
    }

    public R<Void> renameAudioFile(AppFileRenameDto bo) {
        UpdateWrapper<AppBusinessFile> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id",bo.getId());
        updateWrapper.set("re_name",bo.getFileName());
        appBusinessFileMapper.update(updateWrapper);
        return R.ok();
    }
}