package com.mortals.xhx.common.utils;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.mortals.xhx.common.pdu.*;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * 大模型语音识别---方言识别
 */
@Slf4j
public class IatModelMulUtil extends WebSocketListener {
    private final CompletableFuture<String> future = new CompletableFuture<>();
    /*    private static final String hostUrl = "https://iat.cn-huabei-1.xf-yun.com/v1"; // 注意多语种识别，也支持中文音频
        private static final String appid = "3cc52607"; //在控制台-我的应用获取
        private static final String apiSecret = "ZTdmMjFjMGYxYmJhN2VmYjFlMTg3N2Rk"; // 在控制台-我的应用获取
        private static final String apiKey = "d0f73d44e996c2da9924c4476c578a30"; // 在控制台-我的应用获取
        private static final String file = "E://test1.mp3"; // 识别音频位置*/
    public static final int StatusFirstFrame = 0;
    public static final int StatusContinueFrame = 1;
    public static final int StatusLastFrame = 2;

    public static final String encoding = "raw";


    public static final Gson gson = new Gson();
    // 开始时间
    private static Date dateBegin = new Date();
    // 结束时间
    private static Date dateEnd = new Date();
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS");

    private final StringBuilder responseBuilder = new StringBuilder();

    public String file; // 识别音频位置*/

    public String appid;

    private byte[] bytes;


    public IatModelMulUtil(byte[] bytes, String appid) {
        //  this.file = file;
        this.bytes = bytes;
        this.appid = appid;
    }

    public IatModelMulUtil(String file, String appid) {
        this.file = file;
        this.appid = appid;
    }

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        new Thread(() -> {
            //连接成功，开始发送数据
            //int frameSize = 1280; //每一帧音频的大小,建议每 40ms 发送 122B
            int frameSize = 8092; //每一帧音频的大小,建议每 40ms 发送 122B
            int intervel = 40;
            int status = 0;  // 音频的状态
            int seq = 0; //数据序号
            try (FileInputStream fs = new FileInputStream(file)) {
             //   try{
                byte[] buffer = new byte[frameSize];
                // 发送音频
                end:
                while (true) {
                    seq++; // 每次循环更新下seq
                    //int len = bytes.length;


                    int len = fs.read(buffer);
                    //int len = bytes.length;
                    if (len == -1) {
                        status = StatusLastFrame;  //文件读完，改变status 为 2
                    }
                    switch (status) {
                        case StatusFirstFrame:   // 第一帧音频status = 0
                            AudioRoot audioRoot = new AudioRoot();
                            com.mortals.xhx.common.pdu.Header header = new com.mortals.xhx.common.pdu.Header();
                            header.setAppId(appid);
                            header.setStatus(StatusFirstFrame);
                            audioRoot.setHeader(header);

                            Parameter parameter = new Parameter();

                            Iat iat = new Iat();
                            iat.setDomain("slm");
                            iat.setLanguage("zh_cn");
                            iat.setAccent("mulacc");
                            iat.setEos(6000);
                            iat.setVinfo(1);
                            com.mortals.xhx.common.pdu.Result result = new com.mortals.xhx.common.pdu.Result();
                            result.setEncoding("utf8");
                            result.setCompress("raw");
                            result.setFormat("json");
                            iat.setResult(result);
                            parameter.setIat(iat);
                            audioRoot.setParameter(parameter);
                            com.mortals.xhx.common.pdu.Payload payload = new com.mortals.xhx.common.pdu.Payload();

                            Audio audio = new Audio();
                            audio.setEncoding(encoding);
                            audio.setSampleRate(16000);
                            audio.setChannels(1);
                            audio.setBitDepth(16);
                            audio.setSeq(seq);
                            audio.setStatus(status);
                            audio.setAudio(Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)));
                            payload.setAudio(audio);
                            audioRoot.setPayload(payload);

                            String json = JSON.toJSONString(audioRoot);
/*
                            String json = "{\n" +
                                    "  \"header\": {\n" +
                                    "    \"app_id\": \"" + appid + "\",\n" +
                                    "    \"status\": " + StatusFirstFrame + "\n" +
                                    "  },\n" +
                                    "  \"parameter\": {\n" +
                                    "    \"iat\": {\n" +
                                    "      \"domain\": \"slm\",\n" +
                                    "      \"language\": \"zh_cn\",\n" +
                                    "      \"accent\": \"mulacc\",\n" +
                                    "      \"eos\": 6000,\n" +
                                    "      \"vinfo\": 1,\n" +
                                    "      \"result\": {\n" +
                                    "        \"encoding\": \"utf8\",\n" +
                                    "        \"compress\": \"raw\",\n" +
                                    "        \"format\": \"json\"\n" +
                                    "      }\n" +
                                    "    }\n" +
                                    "  },\n" +
                                    "  \"payload\": {\n" +
                                    "    \"audio\": {\n" +
                                    "      \"encoding\": \"raw\",\n" +
                                    "      \"sample_rate\": 16000,\n" +
                                    "      \"channels\": 1,\n" +
                                    "      \"bit_depth\": 16,\n" +
                                    "      \"seq\": " + seq + ",\n" +
                                    "      \"status\": 0,\n" +
                                    "      \"audio\": \"" + Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)) + "\"\n" +
                                    "    }\n" +
                                    "  }\n" +
                                    "}";*/
                            log.info("first json==>" + json);
                            webSocket.send(json);
                            status = StatusContinueFrame;  // 发送完第一帧改变status 为 1
                            break;
                        case StatusContinueFrame:  //中间帧status = 1
                            audioRoot = new AudioRoot();
                            header = new com.mortals.xhx.common.pdu.Header();
                            header.setAppId(appid);
                            header.setStatus(status);
                            audioRoot.setHeader(header);

                            payload = new com.mortals.xhx.common.pdu.Payload();
                            audio = new Audio();
                            audio.setEncoding(encoding);
                            audio.setSampleRate(16000);
                            audio.setChannels(1);
                            audio.setBitDepth(16);
                            audio.setSeq(seq);
                            audio.setStatus(status);
                            audio.setAudio(Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)));
                            payload.setAudio(audio);
                            audioRoot.setPayload(payload);

                            json = JSON.toJSONString(audioRoot);

                          /*  json = "{\n" +
                                    "  \"header\": {\n" +
                                    "    \"app_id\": \"" + appid + "\",\n" +
                                    "    \"status\": 1\n" +
                                    "  },\n" +
                                    "  \"payload\": {\n" +
                                    "    \"audio\": {\n" +
                                    "      \"encoding\":  \"raw\",\n" +
                                    "      \"sample_rate\": 16000,\n" +
                                    "      \"channels\": 1,\n" +
                                    "      \"bit_depth\": 16,\n" +
                                    "      \"seq\": " + seq + ",\n" +
                                    "      \"status\": 1,\n" +
                                    "      \"audio\": \"" + Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)) + "\"\n" +
                                    "    }\n" +
                                    "  }\n" +
                                    "}";*/
                            //  log.info("mid json==>" + json);
                            webSocket.send(json);
                            break;
                        case StatusLastFrame:    // 最后一帧音频status = 2 ，标志音频发送结束
                            audioRoot = new AudioRoot();
                            header = new com.mortals.xhx.common.pdu.Header();
                            header.setAppId(appid);
                            header.setStatus(status);
                            audioRoot.setHeader(header);

                            payload = new com.mortals.xhx.common.pdu.Payload();
                            audio = new Audio();
                            audio.setEncoding(encoding);
                            audio.setSampleRate(16000);
                            audio.setChannels(1);
                            audio.setBitDepth(16);
                            audio.setSeq(seq);
                            audio.setStatus(status);
                            audio.setAudio("");
                            payload.setAudio(audio);
                            audioRoot.setPayload(payload);

                            json = JSON.toJSONString(audioRoot);

                          /*  json = "{\n" +
                                    "  \"header\": {\n" +
                                    "    \"app_id\": \"" + appid + "\",\n" +
                                    "    \"status\": 2\n" +
                                    "  },\n" +
                                    "  \"payload\": {\n" +
                                    "    \"audio\": {\n" +
                                    "      \"encoding\":  \"raw\",\n" +
                                    "      \"sample_rate\": 16000,\n" +
                                    "      \"channels\": 1,\n" +
                                    "      \"bit_depth\": 16,\n" +
                                    "      \"seq\": " + seq + ",\n" +
                                    "      \"status\": 2,\n" +
                                    "      \"audio\": \"\"\n" +
                                    "    }\n" +
                                    "  }\n" +
                                    "}";*/
                            //  log.info("last json==>" + json);
                            webSocket.send(json);
                            break end;
                    }
                    Thread.sleep(intervel); //模拟音频采样延时
                }
            } catch (Exception e) {
                log.info("error==>" + e.getMessage());
            }
        }).start();
    }

    @Override
    public void onMessage(WebSocket webSocket, String text) {
        super.onMessage(webSocket, text);
        // log.info("onMessage==>" + text);
        JsonParse jsonParse = gson.fromJson(text, JsonParse.class);
        if (jsonParse != null) {
            if (jsonParse.header.code != 0) {
                log.error("code=>{} error=>{} sid={}", jsonParse.header.code, jsonParse.header.message, jsonParse.header.sid);
                return;
            }
            if (jsonParse.payload != null) {
                if (jsonParse.payload.result.text != null) { // 中间结果
                    byte[] decodedBytes = Base64.getDecoder().decode(jsonParse.payload.result.text);
                    String decodeRes = new String(decodedBytes, StandardCharsets.UTF_8);

                    JsonParseText jsonParseText = gson.fromJson(decodeRes, JsonParseText.class);
                    List<Ws> wsList = jsonParseText.ws;
                    // log.info("中间识别结果==》");
                    for (Ws ws : wsList) {
                        List<Cw> cwList = ws.cw;
                        for (Cw cw : cwList) {
                            log.info("识别结果==》{}", cw.w);
                            responseBuilder.append(cw.w);
        /*                    try {


                               // emitter.send(cw.w);
                            } catch (IOException e) {
                                log.error("发送异常==》{}", e.getMessage());
                            }*/
                        }
                    }
                }
                if (jsonParse.payload.result.status == 2) { // 最终结果  说明数据全部返回完毕，可以关闭连接，释放资源
                    log.info("session end ");
                    dateEnd = new Date();

                    log.info(sdf.format(dateBegin) + "开始");
                    log.info(sdf.format(dateEnd) + "结束");
                    log.info("耗时:" + (dateEnd.getTime() - dateBegin.getTime()) + "ms");
                    // System.out.println("最终识别结果 ==》" + decodeRes);  // 按照规则替换与追加出最终识别结果
                    log.info("本次识别sid ==》" + jsonParse.header.sid);
                    //  emitter.complete();

                    future.complete(responseBuilder.toString());
                    log.info("emitter 关闭");

                    webSocket.close(1000, "");
                }
            }
        }
    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        try {
            if (null != response) {
                int code = response.code();
                log.info("onFailure code:" + code);
                log.info("onFailure body:" + response.body().string());
     /*           if (101 != code) {
                    System.out.println("connection failed");
                    System.exit(0);
                }*/
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            log.error("onFailure error:" + e.getMessage());
        }

        future.completeExceptionally(t);
    }


    public static 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();
    }

    public CompletableFuture<String> getResultFuture() {
        return future;
    }

    // 返回结果拆分与展示，仅供参考
    // 返回结果拆分与展示，仅供参考
    class JsonParse {
        Header header;
        Payload payload;
    }

    class Header {
        int code;
        String message;
        String sid;
        int status;
    }

    class Payload {
        Result result;
    }

    class Result {
        String text;
        int status;
    }

    class JsonParseText {
        List<Ws> ws;
        String pgs;
        List<Integer> rg;
    }

    class Ws {
        List<Cw> cw;
    }

    class Cw {
        String w;
    }
}
