package admin.server;

import admin.config.SpeechToTextConfig;
import admin.model.WebSocketModel;
import admin.modules.chatgpt.domain.ChatgptRoleRecords;
import admin.modules.chatgpt.domain.ChatgptRoleSession;
import admin.modules.chatgpt.service.ChatgptRoleRecordsService;
import admin.modules.chatgpt.service.ChatgptRoleSessionService;
import admin.service.MaxkbOpenApiService;
import admin.util.DateUtil;
import admin.util.ScreenEnum;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author lj
 * @date 2025/2/20 19:46
 */
@ServerEndpoint("/webSocket/voice/{memberId}")
@Component
@Slf4j
public class VoiceWebSocketServer {
    private static AtomicInteger onlineNum = new AtomicInteger();
    private static SpeechToTextConfig voiceConfig;
    private  static MaxkbOpenApiService maxkbOpenApiService;
    private  static ChatgptRoleRecordsService chatgptRoleRecordsService;
    private  static ChatgptRoleSessionService chatgptRoleSessionService;

    @Resource
    public void setVoiceConfig(SpeechToTextConfig speechToTextConfig){
        voiceConfig = speechToTextConfig;
    }

    @Resource
    public void setChatgptRoleRecordsService(ChatgptRoleRecordsService chatgptRoleRecordsService){
        this.chatgptRoleRecordsService = chatgptRoleRecordsService;
    }

    @Resource
    public void setChatgptRoleSessionService(ChatgptRoleSessionService chatgptRoleSessionService){
        this.chatgptRoleSessionService = chatgptRoleSessionService;
    }


    @Resource
    public void setMaxkbOpenApiService(MaxkbOpenApiService maxkbOpenApiService){
        VoiceWebSocketServer.maxkbOpenApiService = maxkbOpenApiService;
    }

    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    //建立连接成功调用
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "memberId") String memberId) {
        log.info("{}已连接", memberId);
        sessionPools.put(memberId, session);
        //1.获取最新的会话id
        String maxSessionId = chatgptRoleRecordsService.queryMaxSessionId();
        WebSocketModel webSocketModel = new WebSocketModel();
        webSocketModel.setType("text");
        webSocketModel.setAction("replaySessionId");
        webSocketModel.setContent(maxSessionId);
        webSocketModel.setSessionId(maxSessionId);
        //2.返回给前端最新的sessionId
        String msg = JSON.toJSONString(webSocketModel);
        try {
            session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //关闭连接时调用
    @OnClose
    public void onClose(Session session, CloseReason closeReason,@PathParam(value = "memberId") String memberId) {
        sessionPools.remove(memberId);
        log.info("关闭连接");
        System.out.println(sessionPools);
    }

    //收到客户端信息
    @OnMessage(maxMessageSize = 10*1024*1024)
    public void onTextMessage(Session session,String message) throws IOException {
//        session.setMaxBinaryMessageBufferSize(50 * 1024 * 1024);
        log.info("message:{}", message);
        @SuppressWarnings({"unchecked"})
        Map<String, Object> map = JSON.parseObject(message, Map.class);
        WebSocketModel webSocketModel = JSON.parseObject(message, WebSocketModel.class);
        //1.如果问题不存在则创建问题
        ChatgptRoleSession byId = chatgptRoleSessionService.getById(webSocketModel.getSessionId());
        if (byId == null && webSocketModel.getAction().equals("text")) {
            ChatgptRoleSession chatgptRoleSession = new ChatgptRoleSession();
            chatgptRoleSession.setId(Integer.valueOf(webSocketModel.getSessionId()));
            chatgptRoleSession.setName(webSocketModel.getData());
            chatgptRoleSession.setRoleDescId(10001);
            chatgptRoleSession.setUserId(ScreenEnum.getByUserName(webSocketModel.getName()));
            chatgptRoleSessionService.save(chatgptRoleSession);
        } else if (byId != null && webSocketModel.getAction().equals("text")) {
            ChatgptRoleRecords chatgptRoleRecords = new ChatgptRoleRecords();
            chatgptRoleRecords.setMessage(webSocketModel.getData());
            chatgptRoleRecords.setReceiverId(10001);
            chatgptRoleRecords.setSenderId(Integer.valueOf(webSocketModel.getSessionId()));
            chatgptRoleRecords.setCreateTime(DateUtil.getNowTimestamp());
            chatgptRoleRecordsService.save(chatgptRoleRecords);
        }
        String msg = "";
        // 文字聊天
        if ("sendText".equals(map.get("action"))) {
            msg = (String) map.get("data");
            if ("sendText".equals(webSocketModel.getAction())) {
                msg = webSocketModel.getData();
            }
            if (msg != null && !msg.isEmpty()) {
                maxkbOpenApiService.talk(msg, session);
            }
        }
    }

//    @OnMessage
//    public void onBinaryMessage(Session session, ByteBuffer data) {
//        session.setMaxBinaryMessageBufferSize(50 * 1024 * 1024);
//        // 处理二进制音频数据块
//        log.info("data:{}",data);
//        handleAudioChunk(session, data);
//    }


    //错误时调用
    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("发生错误");
        for (Map.Entry<String, Session> map : sessionPools.entrySet()) {
            Session value = map.getValue();
            if (value.equals(session)) {
                sessionPools.remove(map.getKey());
            }
        }
        throwable.printStackTrace();
    }


    //发送消息
    public void sendMessage(Session session, String message) {
        if (session != null) {
            synchronized (session) {
                try {
                    session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 处理音频数据
     * @param session
     * @param data
     */
    private void handleAudioChunk(Session session, ByteBuffer data) {
        //1.音频转文本
        speechToText(data);

        //2.向ai提问获取答案

        //3.文本转语音
        textToSpeech();
        sendMessage(session, null);
    }

    private void textToSpeech() {
    }

    /**
     * 语音转文本
     * @param data   语音数据
     */
    private void speechToText(ByteBuffer data) {
        byte[] bytes = new byte[data.remaining()];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            byteArrayOutputStream.write(bytes);
            String authUrl = null;
            authUrl = getAuthUrl(voiceConfig.getHostUrl(), voiceConfig.getApiKey(), voiceConfig.getApiSecret());
            OkHttpClient client = new OkHttpClient.Builder().build();
            String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
            Request request = new Request.Builder().url(url).build();
            ResponseBody body = client.newCall(request).execute().body();
            System.out.println(body.string());
//            WebSocket webSocket = client.newWebSocket(request, new ResponseData());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
                append("date: ").append(date).append("\n").//
                append("GET ").append(url.getPath()).append(" HTTP/1.1");
        //System.out.println(builder);
        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
        String sha = Base64.getEncoder().encodeToString(hexDigits);

        //System.out.println(sha);
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        //System.out.println(authorization);
        HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().//
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(charset))).//
                addQueryParameter("date", date).//
                addQueryParameter("host", url.getHost()).//
                build();
        return httpUrl.toString();
    }
}