package com.sonic.cow.client.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.sonic.cow.client.VoiceChatClient;
import com.sonic.cow.client.input.voicechat.StartVoiceChatInput;
import com.sonic.cow.client.input.voicechat.StopVoiceChatInput;
import com.sonic.cow.client.input.voicechat.UpdateVoiceChatInput;
import com.sonic.cow.client.input.voicechat.domain.*;
import com.sonic.cow.client.output.voicechat.VoiceChatResponse;
import com.sonic.cow.client.rtc.*;
import com.sonic.cow.client.rtc.callback.conversation.Conv;
import com.sonic.cow.client.rtc.callback.RtsMessage;
import com.sonic.cow.client.rtc.callback.conversation.ConversationMessageParse;
import com.sonic.cow.client.rtc.callback.rts.MessageParse;
import com.sonic.cow.client.rtc.callback.rts.Subv;
import com.sonic.cow.domain.input.VoiceChatOptInput;
import com.sonic.cow.service.CommonSendMqService;
import com.sonic.cow.utils.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class VoiceChatClientImpl implements VoiceChatClient {

    private final StringRedisTemplate stringRedisTemplate;
    @Value("${volcengine.rtc.appId}")
    private String rtcAppId;
    @Value("${volcengine.rtc.appKey}")
    private String rtcAppKey;
    @Value("${volcengine.rtc.url}")
    private String rtcUrl;
    @Value("${volcengine.rtc.action.startVoiceChat}")
    private String startVoiceChatAction;
    @Value("${volcengine.rtc.action.stopVoiceChat}")
    private String stopVoiceChatAction;
    @Value("${volcengine.rtc.action.updateVoiceChat}")
    private String updateVoiceChatAction;
    @Value("${volcengine.rtc.version}")
    private String rtcVersion;

    @Value("${volcengine.accessKey}")
    private String accessKey;
    @Value("${volcengine.secretKey}")
    private String secretKey;
    @Value("${volcengine.region}")
    private String region;
    @Value("${volcengine.rtc.host}")
    private String rtcHost;
    @Value("${volcengine.rtc.service}")
    private String service;
    @Value("${volcengine.asr.appId}")
    private String asrAppId;
    @Value("${volcengine.asr.accessToken}")
    private String asrAccessToken;
    @Value("${volcengine.asr.provider}")
    private String asrProvider;
    @Value("${volcengine.tts.appId}")
    private String ttsAppId;
    @Value("${volcengine.tts.accessToken}")
    private String ttsAccessToken;
    @Value("${volcengine.tts.provider}")
    private String ttsProvider;
    @Value("${volcengine.llm.endPointId}")
    private String llmEndPointId;
    @Value("${volcengine.llm.apiKey}")
    private String llmApiKey;
    @Value("${volcengine.rtc.webhookSecret}")
    private String webhookSecret;

    @Value("${volcengine.rtc.subtitleServiceUrl}")
    private String subtitleServiceUrl;
    @Value("${volcengine.rtc.subtitleServiceSignature}")
    private String subtitleServiceSignature;


    @Autowired
    private CommonSendMqService commonSendMqService;

    public VoiceChatClientImpl(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取公共headers
     *
     * @param body
     * @param action
     * @return
     */
    public Map<String, String> getPublicHeaders(byte[] body, String action) {
        Sign sign = new Sign(region, service, rtcHost, "/", accessKey, secretKey);
        try {
            return sign.getPublicHeaders("POST", Maps.newHashMap(), body, action, rtcVersion);
        } catch (Exception e) {
            log.error("getPublicHeaders error", e);
        }
        return Maps.newHashMap();
    }

    @Override
    public String generateRtcToken(String roomId, String userId) {
        AccessToken token = new AccessToken(rtcAppId, rtcAppKey, roomId, userId);
        //token过期时间
        token.ExpireTime(Utils.getTimestamp() + 3600);
        //添加订阅流权限
        token.AddPrivilege(AccessToken.Privileges.PrivSubscribeStream, 0);
        //添加发布流权限
        token.AddPrivilege(AccessToken.Privileges.PrivPublishStream, Utils.getTimestamp() + 3600);
        return token.Serialize();
    }

    @Override
    public void startVoiceChat(String roomId, String taskId, Audio ttsAudio, String systemPrompt, List<UserPrompt> userPromptList, AgentConfig agentConfig) {
        try {
            //请求url
            String url = String.format(rtcUrl, startVoiceChatAction, rtcVersion);
            //请求入参
            StartVoiceChatInput request = StartVoiceChatInput.builder().build();
            //asr配置
            ASRConfig asrConfig = ASRConfig.getAsrConfig(asrAppId, asrAccessToken, asrProvider);
            //tts配置
            TTSConfig ttsConfig = TTSConfig.getTTSConfig(ttsAppId, ttsAccessToken, ttsAudio, ttsProvider);
            //llm配置
            LLMConfig llmConfig = LLMConfig.getLLMConfig(llmEndPointId, llmApiKey, systemPrompt, userPromptList);
            //实时字幕配置
            SubtitleConfig subtitleConfig = SubtitleConfig.getSubtitleConfig(subtitleServiceUrl, subtitleServiceSignature);
            //配置 智能体交互服务配置，包括语音识别（ASR）、语音合成（TTS）、大模型(LLM)、字幕和函数调用（Function Calling）配置。
            Config config = Config.getConfig(asrConfig, ttsConfig, llmConfig, subtitleConfig);
            //构建完整请求入参
            StartVoiceChatInput startVoiceChatInput = request.buildStartVoiceChatInput(rtcAppId, config, roomId, taskId, agentConfig);
            log.info("startVoiceChat url:{}", url);
            log.info("startVoiceChat startVoiceChatInput:{}", JSON.toJSONString(startVoiceChatInput));
            //发起请求，获取结果
            String response = getRequestResult(url, startVoiceChatInput, startVoiceChatAction);
            log.info("startVoiceChat response: {}", response);
            VoiceChatResponse voiceChatResponse = JSONObject.parseObject(response, VoiceChatResponse.class);
            if (voiceChatResponse.getResult().equals("ok")) {
                log.info("startVoiceChat success");
            }
        } catch (Exception e) {
            log.error("startVoiceChat error:", e);
        }
    }

    @Override
    public void updateVoiceChat(String roomId, String taskId) {
        try {
            //请求url
            String url = String.format(rtcUrl, updateVoiceChatAction, rtcVersion);
            //请求入参
            UpdateVoiceChatInput updateVoiceChatInput = UpdateVoiceChatInput.builder()
                    .AppId(rtcAppId)
                    .RoomId(roomId)
                    .TaskId(taskId)
                    .Command("interrupt")
                    .build();
            log.info("updateVoiceChat url: {}", url);
            log.info("updateVoiceChat updateVoiceChatInput: {}", JSON.toJSONString(updateVoiceChatInput));
            //发起请求，获取结果
            String response = getRequestResult(url, updateVoiceChatInput, updateVoiceChatAction);
            log.info("updateVoiceChat response: {}", response);
            VoiceChatResponse voiceChatResponse = JSONObject.parseObject(response, VoiceChatResponse.class);
            if (voiceChatResponse.getResult().equals("ok")) {
                log.info("updateVoiceChat success");
            }
        } catch (Exception e) {
            log.error("updateVoiceChat error:", e);
        }
    }

    @Override
    public void stopVoiceChat(String roomId, String taskId) {
        try {
            //请求url
            String url = String.format(rtcUrl, stopVoiceChatAction, rtcVersion);
            //请求入参
            StopVoiceChatInput stopVoiceChatInput = StopVoiceChatInput.builder()
                    .AppId(rtcAppId)
                    .RoomId(roomId)
                    .TaskId(taskId)
                    .build();
            log.info("stopVoiceChat url: {}", url);
            log.info("stopVoiceChat stopVoiceChatInput: {}", JSON.toJSONString(stopVoiceChatInput));
            //发起请求，获取结果
            String response = getRequestResult(url, stopVoiceChatInput, stopVoiceChatAction);
            log.info("stopVoiceChat response: {}", response);
            VoiceChatResponse voiceChatResponse = JSONObject.parseObject(response, VoiceChatResponse.class);
            if (voiceChatResponse.getResult().equals("ok")) {
                log.info("stopVoiceChat success");
            }
        } catch (Exception e) {
            log.error("stopVoiceChat error:", e);
        }
    }

    /**
     * 获取请求结果
     *
     * @param url
     * @param requestBody
     * @param action
     * @return
     * @throws UnirestException
     */
    private String getRequestResult(String url, Object requestBody, String action) throws UnirestException {
        //请求body转换为bytes数组
        String jsonStr = JSON.toJSONString(requestBody);
        log.info("getRequestResult body jsonStr:{}", jsonStr);
        byte[] body = jsonStr.getBytes(StandardCharsets.UTF_8);
        //获取公共headers
        Map<String, String> publicHeaders = getPublicHeaders(body, action);
        log.info("getRequestResult publicHeaders:{}", JSON.toJSONString(publicHeaders));
        log.info("getRequestResult body jsonStr2:{}", jsonStr);
        String response = Unirest.post(url)
                .headers(publicHeaders)
                .body(jsonStr)
                .asString().getBody();
        return response;
    }

    @Override
    public void webhook(HttpServletRequest request, HttpServletResponse response) {
        try {
            String body = HttpUtil.getBody(request);
            log.info("VoiceChatClientImpl webhook body:" + body);
            Event event = JSON.parseObject(body, Event.class);
            log.info("VoiceChatClientImpl webhookOutput:" + body);//验签

            response.setContentType("application/json;charset=utf-8");
            if (CheckSignature.check(event, webhookSecret)) {
                //发送MQ,相关业务处理
                commonSendMqService.voiceCallWebhookSendMq(event);
                response.setStatus(200);
            } else {
                response.setStatus(500);
            }
        } catch (Exception e) {
            log.error("webhook error:", e);
        }
    }


    @Override
    public Conv conversationStateCallback(HttpServletRequest request, HttpServletResponse response) {
        try {
            String body = HttpUtil.getBody(request);
            log.info("conversationStateCallback  body:" + body);
            RtsMessage rtsMessage = JSON.parseObject(body, RtsMessage.class);
            String message = rtsMessage.getMessage();
            String signature = rtsMessage.getSignature();
            log.info("conversationStateCallback message:{}", message);
            log.info("conversationStateCallback signature:{}", signature);
            //验证签名
            if (!"123456".equals(signature)) {
                throw new Exception("signature invalid");
            }
            Conv conv = ConversationMessageParse.unpack(message);
            log.info("conversationStateCallback conv:{}", conv);
            return conv;
        } catch (Exception e) {
            log.error("conversationStateCallback error:", e);
        }
        return null;
    }

    @Override
    public Subv rtsCallback(HttpServletRequest request, HttpServletResponse response) {
        try {
            String body = HttpUtil.getBody(request);
            log.info("rtsCallback  body:" + body);
            RtsMessage rtsMessage = JSON.parseObject(body, RtsMessage.class);
            String message = rtsMessage.getMessage();
            String signature = rtsMessage.getSignature();
            log.info("rtsCallback message:{}", message);
            log.info("rtsCallback signature:{}", signature);
            // 验证签名
            if (!subtitleServiceSignature.equals(signature)) {
                throw new Exception("signature invalid");
            }
            //解析消息
            Subv subv = MessageParse.unpack(message);
            log.info("rtsCallback subv:{}", subv);
            return subv;
        } catch (Exception e) {
            log.error("rtsCallback error:", e.getMessage());
        }
        return null;
    }
}
