package com.fs.aiSoundReplication.service.impl;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fs.aiSoundReplication.config.VoiceCloneConfig;
import com.fs.aiSoundReplication.exception.ErrorCodeEnum;
import com.fs.aiSoundReplication.exception.VoiceCloneException;
import com.fs.aiSoundReplication.param.*;
import com.fs.aiSoundReplication.service.VoiceCloneService;
import com.fs.aiSoundReplication.util.FileUtil;
import com.fs.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;

@Service
@Slf4j
public class VoiceCloneServiceImpl implements VoiceCloneService {

    @Autowired
    private VoiceCloneConfig config;

    @Autowired
    private OkHttpClient okHttpClient;

    @Autowired
    private ObjectMapper objectMapper;

    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String RESOURCE_ID_HEADER = "Resource-Id";

    @Override
    public R uploadVoice(String speakerId, MultipartFile audioFile,
                         Integer modelType, Integer language) {
        try {
            // 1. 参数校验
            if (!validateUploadParams(speakerId, audioFile, modelType))
                throw new VoiceCloneException(ErrorCodeEnum.BAD_REQUEST_ERROR.getCode(),ErrorCodeEnum.BAD_REQUEST_ERROR.getMessage());

            // 2. 构建请求
            VoiceCloneRequest request = buildUploadRequest(speakerId, audioFile, modelType, language);
            String requestBody = objectMapper.writeValueAsString(request);

            // 3. 构建请求头
            String resourceId = getResourceIdByModelType(modelType);
            Request httpRequest = buildHttpRequest(config.getUploadUrl(), resourceId, requestBody);

            // 4. 发送请求（带重试机制）
            UploadResponse response = executeRequestWithRetry(httpRequest, UploadResponse.class);

            // 5. 检查响应
            checkResponse(response);

            log.info("音色上传成功，speakerId: {}", response.getSpeakerId());
            return R.ok();

        } catch (JsonProcessingException e) {
            log.error("JSON序列化失败,声音复刻发送豆包失败", e);
            return R.error("JSON序列化失败");
        } catch (IOException e) {
            log.error("文件处理失败,声音复刻发送豆包失败", e);
            return R.error("文件处理失败");
        }
    }

    @Override
    public UploadResponse uploadVoiceByPath(String speakerId, String filePath,
                                            Integer modelType, Integer language) {
        try {
            // 1. 参数校验
            validateUploadParams(speakerId, null, modelType);

            // 2. 读取文件并转换为Base64
            String base64Audio = FileUtil.fileToBase64(filePath);
            String fileExtension = FileUtil.getFileExtension(filePath);

            // 3. 构建音频信息
            VoiceCloneRequest.AudioInfo audioInfo = new VoiceCloneRequest.AudioInfo();
            audioInfo.setAudioBytes(base64Audio);
            audioInfo.setAudioFormat(fileExtension);

            VoiceCloneRequest request = new VoiceCloneRequest();
            request.setAppid(config.getAppId());
            request.setSpeakerId(speakerId);
            request.setAudios(Collections.singletonList(audioInfo));
            request.setModelType(modelType);
            request.setLanguage(language != null ? language : 0);
            request.setSource(2);

            // 4. 构建请求头并发送
            String resourceId = getResourceIdByModelType(modelType);
            String requestBody = objectMapper.writeValueAsString(request);
            Request httpRequest = buildHttpRequest(config.getUploadUrl(), resourceId, requestBody);

            UploadResponse response = executeRequestWithRetry(httpRequest, UploadResponse.class);
            checkResponse(response);

            log.info("音色上传成功（文件路径方式），speakerId: {}", response.getSpeakerId());
            return response;

        } catch (IOException e) {
            log.error("文件处理失败", e);
            throw new VoiceCloneException("文件处理失败", e);
        }
    }

    @Override
    public StatusResponse queryTrainingStatus(String speakerId) {
        try {
            // 1. 参数校验
            if (speakerId == null || speakerId.trim().isEmpty()) {
                throw new VoiceCloneException(1001, "speakerId不能为空");
            }

            // 2. 构建请求
            TrainingStatusRequest request = new TrainingStatusRequest();
            request.setAppid(config.getAppId());
            request.setSpeakerId(speakerId);
            String requestBody = objectMapper.writeValueAsString(request);

            // 3. 根据历史记录确定资源ID，默认使用ICL 2.0
            String resourceId = config.getResourceIdIcl2();
            Request httpRequest = buildHttpRequest(config.getStatusUrl(), resourceId, requestBody);

            // 4. 发送请求
            StatusResponse response = executeRequest(httpRequest, StatusResponse.class);
            checkResponse(response);

            log.debug("训练状态查询成功，speakerId: {}, 状态: {}",
                    response.getSpeakerId(), response.getStatusText());
            return response;

        } catch (IOException e) {
            log.error("JSON序列化失败", e);
            throw new VoiceCloneException("JSON序列化失败", e);
        }
    }

    @Override
    public StatusResponse pollTrainingStatus(String speakerId, Integer maxPollingTimes,
                                             Long pollingInterval) {
        if (maxPollingTimes == null) {
            maxPollingTimes = config.getMaxPollingTimes();
        }
        if (pollingInterval == null) {
            pollingInterval = config.getPollingInterval();
        }

        StatusResponse finalResponse = null;

        for (int i = 0; i < maxPollingTimes; i++) {
            try {
                // 查询状态
                StatusResponse response = queryTrainingStatus(speakerId);
                finalResponse = response;

                // 检查状态
                Integer status = response.getStatus();
                if (status == 2 || status == 4) {
                    log.info("音色训练完成，speakerId: {}, 状态: {}",
                            speakerId, response.getStatusText());
                    return response;
                } else if (status == 3) {
                    log.error("音色训练失败，speakerId: {}", speakerId);
                    throw new VoiceCloneException(ErrorCodeEnum.fromCode(status).getCode(),
                            "训练失败，状态码: " + status);
                } else if (status == 1) {
                    log.info("训练中... (第{}次轮询)", i + 1);
                    // 等待指定间隔
                    Thread.sleep(pollingInterval);
                } else {
                    log.warn("未知状态，停止轮询，状态码: {}", status);
                    return response;
                }

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new VoiceCloneException("轮询被中断", e);
            } catch (Exception e) {
                log.error("第{}次轮询失败", i + 1, e);
                // 非最后一次失败，继续尝试
                if (i < maxPollingTimes - 1) {
                    try {
                        Thread.sleep(pollingInterval);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new VoiceCloneException("轮询被中断", ie);
                    }
                } else {
                    throw e instanceof VoiceCloneException ?
                            (VoiceCloneException) e :
                            new VoiceCloneException("轮询失败", e);
                }
            }
        }

        log.warn("达到最大轮询次数仍未完成，speakerId: {}", speakerId);
        return finalResponse;
    }

    @Override
    public String getResourceIdByModelType(Integer modelType) {
        if (modelType == null) {
            return config.getResourceIdIcl2(); // 默认使用ICL 2.0
        }

        switch (modelType) {
            case 1: // ICL 1.0
                return config.getResourceIdIcl1();
            case 2: // DiT标准版（使用ICL 1.0）
            case 3: // DiT还原版（使用ICL 1.0）
                return config.getResourceIdIcl1();
            case 4: // ICL 2.0
                return config.getResourceIdIcl2();
            default:
                log.warn("未知的modelType: {}，使用默认ICL 2.0", modelType);
                return config.getResourceIdIcl2();
        }
    }

    // ============ 私有方法 ============

    private boolean validateUploadParams(String speakerId, MultipartFile audioFile,
                                      Integer modelType) {
        if (speakerId == null || speakerId.trim().isEmpty()) {
            log.error("参数 speakerId 不能为空");
            return false;
        }

        if (audioFile != null && audioFile.isEmpty()) {
            log.error("参数 audioFile 不能为空");
            return false;
        }

        if (modelType != null && modelType < 0 || modelType > 4) {
            log.error("参数 modelType 错误，应为1-4");
            return false;
        }
        return true;
    }

    private VoiceCloneRequest buildUploadRequest(String speakerId, MultipartFile audioFile,
                                                 Integer modelType, Integer language) throws IOException {
        VoiceCloneRequest request = new VoiceCloneRequest();
        request.setAppid(config.getAppId());
        request.setSpeakerId(speakerId);
        request.setModelType(modelType != null ? modelType : 4);
        request.setLanguage(language != null ? language : 0);
        request.setSource(2);

        // 构建音频信息
        VoiceCloneRequest.AudioInfo audioInfo = new VoiceCloneRequest.AudioInfo();
        audioInfo.setAudioBytes(FileUtil.multipartFileToBase64(audioFile));

        // 获取文件扩展名
        String originalFilename = audioFile.getOriginalFilename();
        String fileExtension = FileUtil.getFileExtension(originalFilename);
        if (fileExtension != null){
            if (fileExtension.equals("m4a") || fileExtension.equals("pcm"))
                audioInfo.setAudioFormat(fileExtension);
        }
        request.setAudios(Collections.singletonList(audioInfo));

//        // 设置额外参数-降噪
//        Map<String, Object> extraParams = new HashMap<>();
//        // ICL 2.0默认关闭降噪以获得更多细节
//        if (modelType == null || modelType == 4) {
//            extraParams.put("enable_audio_denoise", false);
//        } else {
//            extraParams.put("enable_audio_denoise", true);
//        }
//        request.setExtraParams(objectMapper.writeValueAsString(extraParams));

        return request;
    }

    private Request buildHttpRequest(String url, String resourceId, String requestBody) {
        RequestBody body = RequestBody.create(
                MediaType.get("application/json; charset=utf-8"),requestBody
        );

        return new Request.Builder()
                .url(url)
                .post(body)
                .addHeader(AUTHORIZATION_HEADER, "Bearer;" + config.getAccessToken())
                .addHeader(RESOURCE_ID_HEADER, resourceId)
                .build();
    }

    private <T extends BaseResponse> T executeRequest(Request httpRequest, Class<T> responseType)
            throws IOException {
        try (Response response = okHttpClient.newCall(httpRequest).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("HTTP请求失败，状态码: " + response.code());
            }

            String responseBody = response.body().string();
            log.debug("API响应: {}", responseBody);

            return objectMapper.readValue(responseBody, responseType);
        }
    }

    private <T extends BaseResponse> T executeRequestWithRetry(Request httpRequest,
                                                               Class<T> responseType) {
        IOException lastException = null;

        for (int i = 0; i < config.getMaxRetryTimes(); i++) {
            try {
                return executeRequest(httpRequest, responseType);
            } catch (IOException e) {
                lastException = e;
                log.warn("第{}次请求失败，准备重试: {}", i + 1, e.getMessage());

                if (i < config.getMaxRetryTimes() - 1) {
                    try {
                        Thread.sleep(config.getRetryInterval());
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new VoiceCloneException("重试被中断", ie);
                    }
                }
            }
        }

        throw new VoiceCloneException("请求失败，达到最大重试次数", lastException);
    }

    private void checkResponse(BaseResponse response) {
        if (response == null || response.getBaseResp() == null) {
            throw new VoiceCloneException(-1, "响应数据异常");
        }

        Integer statusCode = response.getBaseResp().getStatusCode();
        if (statusCode != 0) {
            String errorMessage = response.getBaseResp().getStatusMessage();
            ErrorCodeEnum errorCode = ErrorCodeEnum.fromCode(statusCode);

            throw new VoiceCloneException(statusCode,
                    String.format("%s (错误码: %d)", errorCode.getMessage(), statusCode));
        }
    }
}
