package com.seer.teach.teacher.service.platform.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.seer.teach.common.enums.AiPlatformEnum;
import com.seer.teach.teacher.common.llm.sse.req.Message;
import com.seer.teach.teacher.common.llm.sse.req.Text;
import com.seer.teach.teacher.service.platform.AiPlatformCallService;
import com.seer.teach.teacher.service.platform.AudioWord;
import com.seer.teach.teacher.service.platform.LlmRequest;
import com.seer.teach.teacher.service.platform.LlmResponse;
import com.seer.teach.teacher.service.platform.StreamingTextToAudioResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.SignalType;
import reactor.core.publisher.Sinks;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 本地模型调用服务实现
 */
@Slf4j
@Service
public class LocalModelPlatformCallServiceImpl implements AiPlatformCallService {

    @Autowired
    private WebClient webClient;

    @Override
    public Optional<LlmResponse> callTextModel(LlmRequest request) {
        try (CloseableHttpClient httpClient = HttpClients.custom().build()) {
            // 构建请求体
            String url = request.getUrl();

            log.info("调用大模型接口，URL: {}", url);
            String modelIdentifier = request.getModel();
            if (!StringUtils.hasText(modelIdentifier)) {
                modelIdentifier = "Qwen/Qwen2.5-VL-32B-Instruct";
            }
            HttpPost post = new HttpPost(url);
            post.addHeader("Content-Type", "application/json");
            post.addHeader("Authorization", "Bearer " + request.getApiKey());

            // 使用ObjectMapper构建安全的JSON请求体
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            ObjectNode requestBody = mapper.createObjectNode();
            requestBody.put("model", modelIdentifier);
            requestBody.put("stream", request.getStream() != null ? request.getStream() : false);
            // 设置模型参数
            if (request.getTemperature() != null) {
                requestBody.put("temperature", request.getTemperature());
            }
            if (request.getTopP() != null) {
                requestBody.put("top_p", request.getTopP());
            }

            ArrayNode messages = mapper.createArrayNode();
            if (CollectionUtil.isNotEmpty(request.getMessages())) {
                for (Message message : request.getMessages()) {
                    ObjectNode msg = mapper.createObjectNode();
                    msg.put("role", message.getRole());
                    List<Text> contentList = message.getContent();
                    msg.set("content", mapper.valueToTree(contentList));
                    messages.add(msg);
                }
            } else {
                ObjectNode message = mapper.createObjectNode();
                message.put("role", "user");
                message.put("content", request.getPrompt());
                messages.add(message);
            }

            requestBody.set("messages", messages);

            if (request.getTemperature() != null) {
                requestBody.put("temperature", request.getTemperature());
            }

            if (request.getTopP() != null) {
                requestBody.put("top_p", request.getTopP());
            }

            String payload = mapper.writeValueAsString(requestBody);
            log.info("请求体: {}", payload);
            post.setEntity(new StringEntity(payload, StandardCharsets.UTF_8));

            HttpResponse response = httpClient.execute(post);
            HttpEntity entity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200 && entity != null) {
                String responseBody = EntityUtils.toString(entity);
                log.info("大模型API调用成功，响应: {}", responseBody);
                LlmResponse llmResponse = parseLLMResponse(responseBody);
                return Optional.of(llmResponse);
            } else {
                log.error("大模型API调用失败，状态码: {}", response.getStatusLine().getStatusCode());
                log.info("响应: {}", response);
            }
            return Optional.empty();
        } catch (IOException e) {
            log.error("调用本地模型API时发生异常", e);
            return Optional.empty();
        }
    }

    @Override
    public Optional<String> asyncCallImageModel(LlmRequest request) {
        log.info("异步调用本地图像模型: {}", request.getPrompt());
        return Optional.empty();
    }

    @Override
    public String getSupportedPlatform() {
        return AiPlatformEnum.LOCAL.getCode();
    }

    private LlmResponse parseLLMResponse(String responseBody) {
        LlmResponse llmResponse = new LlmResponse();
        try {
            // 解析JSON响应
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(responseBody);

            // 提取基本字段
            if (rootNode.has("id")) {
                llmResponse.setId(rootNode.get("id").asText());
            }

            if (rootNode.has("model")) {
                llmResponse.setModel(rootNode.get("model").asText());
            }

            // 提取choices
            JsonNode choices = rootNode.get("choices");
            if (choices != null && choices.isArray() && choices.size() > 0) {
                List<LlmResponse.Choice> choiceList = new ArrayList<>();
                for (JsonNode choiceNode : choices) {
                    LlmResponse.Choice choice = new LlmResponse.Choice();
                    if (choiceNode.has("index")) {
                        choice.setIndex(choiceNode.get("index").asInt());
                    }

                    if (choiceNode.has("finish_reason")) {
                        choice.setFinishReason(choiceNode.get("finish_reason").asText());
                    }

                    if (choiceNode.has("message")) {
                        JsonNode messageNode = choiceNode.get("message");
                        LlmResponse.Message message = new LlmResponse.Message();
                        if (messageNode.has("role")) {
                            message.setRole(messageNode.get("role").asText());
                        }
                        if (messageNode.has("content")) {
                            message.setContent(messageNode.get("content").asText());
                        }
                        choice.setMessage(message);
                    }
                    choiceList.add(choice);
                }
                llmResponse.setChoices(choiceList);
            }

            // 提取usage
            if (rootNode.has("usage")) {
                JsonNode usageNode = rootNode.get("usage");
                LlmResponse.Usage usage = new LlmResponse.Usage();
                if (usageNode.has("prompt_tokens")) {
                    usage.setPromptTokens(usageNode.get("prompt_tokens").asInt());
                }
                if (usageNode.has("completion_tokens")) {
                    usage.setCompletionTokens(usageNode.get("completion_tokens").asInt());
                }
                if (usageNode.has("total_tokens")) {
                    usage.setTotalTokens(usageNode.get("total_tokens").asInt());
                }
                llmResponse.setUsage(usage);
            }

            return llmResponse;
        } catch (Exception e) {
            log.error("解析大模型响应时发生异常", e);
            return llmResponse;
        }
    }

    @Override
    public Queue<StreamingTextToAudioResult> callText2Audio(LlmRequest request) {
        // 用于收集音频数据的队列
        ConcurrentLinkedQueue<StreamingTextToAudioResult> audioDataQueue = new ConcurrentLinkedQueue<>();

        try {
            // 使用WebClient进行流式调用
            Flux<String> responseFlux = callTtsService(request);

            // 订阅并收集所有响应数据
            responseFlux.doOnNext(chunk -> {
                StreamingTextToAudioResult result = parseStreamingTextToAudioResult(chunk);
                if (result != null) {
                    audioDataQueue.add(result);
                }
            })
            .doOnError(error -> log.error("TTS流式调用过程中发生错误", error))
            .doOnComplete(() -> log.info("callText2Audio TTS流式调用完成"))
            .collectList()
            .block();

        } catch (Exception e) {
            log.error("本地TTS接口调用失败", e);
        }

        return audioDataQueue;
    }
    
    @Override
    public Flux<ServerSentEvent<StreamingTextToAudioResult>> streamText2Audio(LlmRequest request) {
        // 创建一个Sinks.Many用于发射数据
        Sinks.Many<ServerSentEvent<StreamingTextToAudioResult>> sink = Sinks.many().unicast().onBackpressureBuffer();
    
        try {
            // 使用WebClient进行流式调用
            Flux<String> responseFlux = callTtsService(request);

            // 处理每个接收到的数据块
            // 修复：保存Disposable引用以防止被垃圾回收并允许取消订阅
            Disposable disposable = responseFlux.subscribe(
                chunk -> {
                    StreamingTextToAudioResult result = parseStreamingTextToAudioResult(chunk);
                    if (result != null) {
                        ServerSentEvent<StreamingTextToAudioResult> event = ServerSentEvent.builder(result).build();
                        sink.tryEmitNext(event);
                    }
                },
                error -> {
                    log.error("TTS流式调用过程中发生错误", error);
                    sink.tryEmitError(error);
                },
                () -> handleStreamCompletion(sink)
            );
        
            // 返回flux并在取消订阅时处理资源清理
            return sink.asFlux()
                    .doFinally(signalType -> {
                        if (signalType == SignalType.CANCEL) {
                            log.info("用户取消了本地TTS流，正在清理资源");
                            if (disposable != null && !disposable.isDisposed()) {
                                disposable.dispose();
                            }
                        }
                    });
        } catch (Exception e) {
            log.error("本地TTS接口调用失败", e);
            sink.tryEmitError(e);
            return sink.asFlux();
        }
    }
    
    /**
     * 处理流完成事件
     * 
     * @param sink 数据发射器
     */
    private void handleStreamCompletion(Sinks.Many<ServerSentEvent<StreamingTextToAudioResult>> sink) {
        log.info("TTS流式调用完成");
        
        // 发送最后一个数据包，标识流已完成
        StreamingTextToAudioResult finalResult = new StreamingTextToAudioResult();
        AudioWord finalWord = new AudioWord();
        finalWord.setFinal(true);
        finalResult.setWord(finalWord);
        finalResult.setFinal(true);
        finalResult.setStreamingCompleted(true);
        ServerSentEvent<StreamingTextToAudioResult> finalEvent = ServerSentEvent.builder(finalResult).event("complete").build();
        sink.tryEmitNext(finalEvent);
        
        sink.tryEmitComplete();
    }

    /**
     * 调用TTS服务
     *
     * @param request 请求参数
     * @return 响应数据流
     */
    private Flux<String> callTtsService(LlmRequest request) {
        String ttsUrl = request.getUrl();
        if(!StringUtils.hasText(ttsUrl)){
            ttsUrl = "http://192.168.0.28:8891/tts-ahom";
        }
        // 构造请求体
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode requestBody = mapper.createObjectNode();
        requestBody.put("userId", 0);
        requestBody.put("text", request.getText());

        // 使用WebClient进行流式调用
        return webClient.post()
                .uri(ttsUrl)
                .header("Content-Type", "application/json")
                .bodyValue(requestBody)
                .retrieve()
                .bodyToFlux(String.class);
    }

    /**
     * 解析流式文本转音频结果
     *
     * @param content 响应数据块
     * @return 解析后的结果对象
     */
    private StreamingTextToAudioResult parseStreamingTextToAudioResult(String content) {
        if (org.apache.commons.lang3.StringUtils.isEmpty(content)) {
            return null;
        }
        log.info("本地TTS接口响应内容: {}", content);
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(content);
            
            StreamingTextToAudioResult result = new StreamingTextToAudioResult();
            
            // 处理音频数据节点
            JsonNode audioDataNode = rootNode.get("audioData");
            if (audioDataNode != null && audioDataNode.isTextual()) {
                result.setAudioText(audioDataNode.asText());
            }
            // 检查是否是最终结果
            JsonNode isFinalNode = rootNode.get("isFinal");
            boolean isFinal = isFinalNode != null && isFinalNode.asBoolean();
            result.setFinal(isFinal);

            // 创建单个AudioWord对象
            AudioWord audioWord = new AudioWord();

            JsonNode frameIndex = rootNode.get("frameIndex");
            JsonNode chunkIndex = rootNode.get("chunkIndex");
            if (frameIndex != null && frameIndex.isNumber()) {
                log.debug("当前帧索引: {}", frameIndex.asInt());
                audioWord.setFrameIndex(frameIndex.asInt());
                audioWord.setChunkIndex(chunkIndex.asInt());
            }
            JsonNode textNode = rootNode.get("text");
            if (textNode != null && textNode.isTextual()) {
                log.debug("当前帧文本: {}", textNode.asText());
                audioWord.setText(textNode.asText());
            }
            result.setWord(audioWord);
            return result;
        } catch (Exception e) {
            log.warn("解析TTS响应时出现错误: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 保存音频数据到本地文件
     * @param text 原始文本
     * @param audioDataQueue 音频数据队列
     */
    private void saveAudioDataToLocalFile(String text, ConcurrentLinkedQueue<StreamingTextToAudioResult> audioDataQueue) {
        try {
            log.info("开始保存音频数据到本地文件");
            
            // 将队列转换为列表
            List<StreamingTextToAudioResult> audioDataList = new ArrayList<>(audioDataQueue);
            
            // 合并所有音频数据为一个完整的音频
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            
            // WAV文件头信息（44字节）
            // RIFF header
            outputStream.write("RIFF".getBytes(StandardCharsets.US_ASCII));
            outputStream.write(intToLittleEndian(0)); // Placeholder for file size
            outputStream.write("WAVE".getBytes(StandardCharsets.US_ASCII));
            
            // Format chunk
            outputStream.write("fmt ".getBytes(StandardCharsets.US_ASCII));
            outputStream.write(intToLittleEndian(16)); // Chunk size
            outputStream.write(shortToLittleEndian((short) 1)); // Audio format (1 = PCM)
            outputStream.write(shortToLittleEndian((short) 1)); // Number of channels (1 = mono)
            outputStream.write(intToLittleEndian(22050)); // Sample rate
            outputStream.write(intToLittleEndian(22050 * 2)); // Byte rate (sample rate * block align)
            outputStream.write(shortToLittleEndian((short) 2)); // Block align (channels * bits per sample / 8)
            outputStream.write(shortToLittleEndian((short) 16)); // Bits per sample
            
            // Data chunk
            outputStream.write("data".getBytes(StandardCharsets.US_ASCII));
            outputStream.write(intToLittleEndian(0)); // Placeholder for data size
            
            int dataSize = 0;
            
            for (StreamingTextToAudioResult result : audioDataList) {
                // 处理音频数据
                if (result.getAudioData() != null) {
                    byte[] audioBytes = new byte[result.getAudioData().remaining()];
                    result.getAudioData().get(audioBytes);
                    outputStream.write(audioBytes);
                    dataSize += audioBytes.length;
                }
            }
            
            // Update file size and data size in the header
            byte[] wavData = outputStream.toByteArray();
            // File size (total size - 8 bytes for RIFF header)
            byte[] fileSizeBytes = intToLittleEndian(dataSize + 36);
            System.arraycopy(fileSizeBytes, 0, wavData, 4, 4);
            // Data size
            byte[] dataSizeBytes = intToLittleEndian(dataSize);
            System.arraycopy(dataSizeBytes, 0, wavData, 40, 4);
            
            // 生成文件名
            String fileName = "tts_" + System.currentTimeMillis() + ".wav";

            String projectPath = System.getProperty("user.dir");
            String audioDirPath = projectPath + "/audios";

            String filePath = audioDirPath + "/" + fileName;

            java.io.File audioDir = new java.io.File(audioDirPath);
            if (!audioDir.exists()) {
                audioDir.mkdirs();
            }
            // 保存音频文件
            try (java.io.FileOutputStream fileOutputStream = new java.io.FileOutputStream(filePath)) {
                fileOutputStream.write(wavData);
                fileOutputStream.flush();
            }
            
            log.info("音频文件保存完成，路径: {}, 大小: {} 字节", audioDirPath, wavData.length);
            
            // 保存字幕文件
            StringBuilder subtitleBuilder = new StringBuilder();
            for (StreamingTextToAudioResult result : audioDataList) {
                if (result.getWord() != null && result.getWord().getText() != null) {
                    subtitleBuilder.append(result.getWord().getText());
                }
            }
            
            String subtitles = subtitleBuilder.toString();
            String subtitleFilePath = filePath.replace(".wav", ".txt");
            try (java.io.FileWriter writer = new java.io.FileWriter(subtitleFilePath)) {
                writer.write(subtitles);
            }
            
            log.info("字幕文件保存完成，路径: {}, 内容: {}", subtitleFilePath, subtitles);
        } catch (Exception e) {
            log.error("保存音频数据到本地文件时发生错误", e);
        }
    }
    
    /**
     * 将整数转换为小端序字节数组
     * @param value 要转换的整数
     * @return 小端序字节数组
     */
    private byte[] intToLittleEndian(int value) {
        return new byte[] {
            (byte) (value & 0xff),
            (byte) ((value >> 8) & 0xff),
            (byte) ((value >> 16) & 0xff),
            (byte) ((value >> 24) & 0xff)
        };
    }
    
    /**
     * 将短整数转换为小端序字节数组
     * @param value 要转换的短整数
     * @return 小端序字节数组
     */
    private byte[] shortToLittleEndian(short value) {
        return new byte[] {
            (byte) (value & 0xff),
            (byte) ((value >> 8) & 0xff)
        };
    }
}