package com.aliyun.sca.sample;

import com.alibaba.fastjson2.JSON;
import com.aliyun.qualitycheck20190115.Client;
import com.aliyun.qualitycheck20190115.models.ApplyWsTokenRequest;
import com.aliyun.qualitycheck20190115.models.ApplyWsTokenResponse;
import com.aliyun.qualitycheck20190115.models.ApplyWsTokenResponseBody;
import com.aliyun.sca.domain.request.ConversationInfo;
import com.aliyun.sca.domain.request.WebSocketMessage;
import com.aliyun.sca.domain.response.WebSocketResponse;
import com.aliyun.sca.enums.ApiTypeEnum;
import com.aliyun.sca.enums.RoleEnum;
import com.aliyun.sca.listener.WebSocketListener;
import com.aliyun.sca.client.ScaWebSocketClient;
import com.aliyun.teaopenapi.models.Config;
import org.apache.http.client.utils.DateUtils;

import java.io.FileInputStream;
import java.net.URI;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * This demo showcases how to use Alibaba Cloud's SmartConversationAnalysis real-time speech recognition and quality check.
 */
public class SoundRecordSample {
    /**
     * 心跳消息
     */
    private static final String HEARTBEAT_MESSAGE = "HeartBeat";

    /**
     * 存储所有的client
     */
    private static ConcurrentHashMap<String, ScaWebSocketClient> CLIENT_MAP = new ConcurrentHashMap<>();

    public static void main(String[] args) throws Exception{
        run();
    }

    private static void run() throws Exception {
        Long baseMeAgentId = 0L;
        //获取token
        ConversationInfo conversationInfo = ConversationInfo.builder()
                .tid(DateUtils.formatDate(new Date(), "yyyyMMdd") + UUID.randomUUID())
                .taskConfigId(0L)
                .business("test")
                .callType(1)
                .caller("*")
                .callee("*")
                .skillGroupId(1L).skillGroupName("test")
                .build();
        Config config = new Config().setAccessKeyId("*").setAccessKeySecret("*");
        config.endpoint = "qualitycheck.cn-hangzhou.aliyuncs.com";
        Client popClient = new Client(config);

        ApplyWsTokenRequest request = new ApplyWsTokenRequest();
        request.setBaseMeAgentId(baseMeAgentId);
        request.setJsonStr(JSON.toJSONString(conversationInfo));
        ApplyWsTokenResponse response = popClient.applyWsToken(request);
        if (null == response || null == response.getBody() || null == response.getBody().getData()) {
            throw new RuntimeException("获取token失败");
        }

        ApplyWsTokenResponseBody.ApplyWsTokenResponseBodyData data = response.getBody().getData();
        // 连接websocket
        URI uri = new URI(getWsUrl(data.getWsEndpoint(), data.getToken(), data.getSessionId()));
        ScaWebSocketClient client = new ScaWebSocketClient(uri, getListener(data.getSessionId()));
        client.connect();

        if (!client.getOpenFlag().await(10, TimeUnit.SECONDS)) {
            throw new RuntimeException("连接失败");
        }

        CLIENT_MAP.put(data.getSessionId(), client);
        //保持在等待消息时，发送心跳
        startTimer(client);

        //开启识别 - 用户
        int sampleRate = 8000;
        WebSocketMessage userWebSocketMessageStart = WebSocketMessage.packAsrStart(ApiTypeEnum.START_TRANS, RoleEnum.USER, sampleRate);
        client.send(JSON.toJSONString(userWebSocketMessageStart));

        //开启识别 - 坐席
        WebSocketMessage agentWebSocketMessageStart = WebSocketMessage.packAsrStart(ApiTypeEnum.START_TRANS, RoleEnum.AGENT, sampleRate);
        client.send(JSON.toJSONString(agentWebSocketMessageStart));

        //发送语音流
        WebSocketMessage userWebSocketMessage = WebSocketMessage.packAudioData(ApiTypeEnum.LIVE_VOICE_TRANS, RoleEnum.USER, 12000, "testUser", "testUserIdentity");
        WebSocketMessage agentWebSocketMessage = WebSocketMessage.packAudioData(ApiTypeEnum.LIVE_VOICE_TRANS, RoleEnum.AGENT, 12001, "testAgent", "testAgentIdentity");
        Thread.sleep(50);

        //发送语音文件，实时语音流无需等待
        int origialLen = 3000;
        try(FileInputStream fis = new FileInputStream(ClassLoader.getSystemResource("file").getPath() + "/" + "maoyi.wav")) {
            byte[] b = new byte[origialLen];
            while (fis.read(b) > 0) {
                userWebSocketMessage.setData(b);
                client.send(JSON.toJSONString(userWebSocketMessage));

                agentWebSocketMessage.setData(b);
                client.send(JSON.toJSONString(agentWebSocketMessage));

                //实时语音不需要等待
                int sleepMs = getSendAudioSleepTime(origialLen, sampleRate, 1);
                Thread.sleep(sleepMs);
            }
        }

        //关闭识别 - 用户
        WebSocketMessage userWebSocketMessageClose = WebSocketMessage.packageAsrClose(ApiTypeEnum.CLOSE_TRANS, RoleEnum.USER);
        client.send(JSON.toJSONString(userWebSocketMessageClose));

        //关闭识别 - 坐席
        WebSocketMessage agentWebSocketMessageClose = WebSocketMessage.packageAsrClose(ApiTypeEnum.CLOSE_TRANS, RoleEnum.AGENT);
        client.send(JSON.toJSONString(agentWebSocketMessageClose));

        //更新质检状态
        //url必须传入录音文件地址
        String url = "http://localhost:80";
        WebSocketMessage webSocketMessageUpdateState = WebSocketMessage.packUpdateQuality(ApiTypeEnum.UPDATE_QUALITY_STATE, url);
        client.send(JSON.toJSONString(webSocketMessageUpdateState));
    }

    private static int getSendAudioSleepTime(int dataSize,
                                     int sampleRate,
                                     int compressRate) {
        // 仅支持16位采样
        int sampleBytes = 16;
        // 仅支持单通道
        int soundChannel = 1;

        // 当前采样率，采样位数下每秒采样数据的大小
        int bytes = (sampleRate * sampleBytes * soundChannel) / 8;

        // 当前采样率，采样位数下每毫秒采样数据的大小
        int bytesMs = bytes / 1000;

        // 待发送数据大小除以每毫秒采样数据大小，以获取sleep时间
        int sleepMs = (dataSize * compressRate) / bytesMs;

        return sleepMs;
    }

    private static WebSocketListener getListener(String sessionId) {
        return new WebSocketListener() {
            @Override
            public void transStart(WebSocketResponse<Object> response) {
                System.out.println(sessionId + " - transStart:" + JSON.toJSONString(response));
            }

            @Override
            public void qualityCheckResut(WebSocketResponse<Object> response) {
                System.out.println(sessionId + " - qualityCheckResut:" + JSON.toJSONString(response));
            }

            @Override
            public void transClose(WebSocketResponse<Object> response) {
                System.out.println(sessionId + " - transClose:" + JSON.toJSONString(response));
            }

            @Override
            public void qualityComplete(WebSocketResponse<Object> response) {
                System.out.println(sessionId + " - qualityComplete:" + JSON.toJSONString(response));
                if (RoleEnum.USER.getName().equals(response.getRole())) {
                    CLIENT_MAP.get(sessionId).setComleteQualityUser(true);
                } else if (RoleEnum.AGENT.getName().equals(response.getRole())) {
                    CLIENT_MAP.get(sessionId).setComleteQualityAgent(true);
                }

            }

            @Override
            public void updateQualityResult(WebSocketResponse<Object> response) {
                System.out.println(sessionId + " - updateQualityResult:" + JSON.toJSONString(response));
                CLIENT_MAP.get(sessionId).setCompleteUpdate(true);
            }

            @Override
            public void onFail(WebSocketResponse<Object> response) {
                System.out.println(sessionId + " - onFail:" + JSON.toJSONString(response));
                ScaWebSocketClient client = CLIENT_MAP.get(sessionId);
                client.close(10, response.getMessage());
                System.out.println("==close==");
            }
        };
    }

    private static void startTimer(ScaWebSocketClient client) {
        new Thread(() -> {
            Thread.currentThread().setName("websocketTimer-" + client.getLastMessageTime());
            int count = 0;
            while (!client.isComleteQualityUser() || !client.isComleteQualityAgent() || !client.isCompleteUpdate()) {
                try {
                    Thread.sleep(1000);
                    if (++count < 20 && System.currentTimeMillis() - client.getLastMessageTime() > 5 * 1000) {
                        client.send(HEARTBEAT_MESSAGE);
                        break;
                    }
                } catch (InterruptedException e) {
                    break;
                }
            }

            if (null != client) {
                System.out.println("==close==");
                client.close();
            }

            System.out.println("==remove==");

        }).start();

    }


    private static String getWsUrl(String wsUrl, String token, String sessionId) {
        return wsUrl + "/" + token + "/" + sessionId;
    }
}
