package org.matrix.socket;

import cn.hutool.log.LogFactory;
import cn.hutool.log.level.Level;
import com.alibaba.fastjson.JSONObject;
import org.matrix.actuators.util.ThreadUtil;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.enums.ExecutionRecType;
import org.matrix.exception.GlobalException;
import org.matrix.util.SpringUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 运行态日志消息队列
 *
 * @author huangxiahao
 */
public class LogQueueRuntime {

    private static final ConcurrentHashMap<Long, TestExecuteLog> LOG_MAP = new ConcurrentHashMap<>();

    private static IExecutionRecordService EXECUTION_RECORD_SERVICE;

    public static IExecutionRecordService getExecutionRecordService(){
        if (EXECUTION_RECORD_SERVICE==null){
            EXECUTION_RECORD_SERVICE = SpringUtils.getBean("executionRecordImpl");
        }
       return EXECUTION_RECORD_SERVICE;
    }

    public static void addExecutionRecord(TestExecuteLog testExecuteLog,String log){
        getExecutionRecordService().addExecutionRecord(testExecuteLog,log);
    }


    /**
     * 如果返回false,表示该用例已经在线程中执行了
     */
    public static void put(Long threadId, TestExecuteLog testExecuteLog) {
        LOG_MAP.put(threadId, testExecuteLog);
    }

    public static void setTestData(Long dataId){
        Long currentThreadId = ThreadUtil.currentThreadId();
        LOG_MAP.get(currentThreadId).setTestDataId(dataId);
    }

    public static void clearTestData(){
        Long currentThreadId = ThreadUtil.currentThreadId();
        LOG_MAP.get(currentThreadId).setTestDataId(-1L);
    }

    public static void addNewLog(String log) {
        Long currentThreadId = ThreadUtil.currentThreadId();
        TestExecuteLog testExecuteLog = LOG_MAP.get(currentThreadId);
        if (testExecuteLog !=null){
            testExecuteLog.addLog(log);
            sendMessage(currentThreadId,testExecuteLog.getUnionKey(),log);
            addExecutionRecord(testExecuteLog,log);
        }else {
            throw new GlobalException("该线程中的TestCaseLog对象未初始化，请先调用put方法初始化对象");
        }
    }

    public static void remove(Long threadId) {
        LOG_MAP.remove(threadId);
    }

    public static void sendMessage(Long threadId,String unionKey, Object log) {
            try {
                TestExecuteLog testExecuteLog = LOG_MAP.get(threadId);
                // 测试任务ID.测试用例ID.测试数据ID
                String message = JSONObject.toJSONString(log);
                String logMessage =
                        String.format(
                                "%s.%s.%s.%s"
                                ,testExecuteLog.getJobId()
                                ,testExecuteLog.getTestCaseId()
                                ,testExecuteLog.getTestDataId()
                                ,message.substring(1,message.length()-1)
                        );
                //如果unionKey中存在监听的socket则向该socket发送消息
                List<WebSocketSession> webSocketSessions = ExecuteMonitorSocketPool.get(unionKey);
                if (webSocketSessions!=null){
                    for (WebSocketSession webSocketSession : webSocketSessions) {
                        webSocketSession.sendMessage(new TextMessage(
                                logMessage
                        ));
                    }
                }
                //如果线程中存在socket则向该socket发送消息
                WebSocketSession webSocketSession = TestCaseExecuteSocketPool.get(threadId);
                if (webSocketSession != null) {
                    webSocketSession.sendMessage(new TextMessage(
                            logMessage
                    ));
                }
            } catch (Exception e) {
                LogFactory.get().log(Level.INFO,"发送socket消息失败,socket已经断开连接");
                remove(threadId);
            }
    }

    public static void initTestCaseLog(Long jobId,Long userId, Long caseId,TestExecuteType type,String unionKey) {
        TestExecuteLog testExecuteLog = new TestExecuteLog();
        testExecuteLog.setJobId(jobId);
        testExecuteLog.setTestCaseId(caseId);
        testExecuteLog.setUserId(userId);
        testExecuteLog.setType(type);
        testExecuteLog.setUnionKey(unionKey);
        if (checkIsInRun(testExecuteLog)){
            throw new GlobalException("当前用例正在执行中");
        }
        LogQueueRuntime.put(ThreadUtil.currentThreadId(), testExecuteLog);
    }

    public static Boolean checkIsInRun(TestExecuteLog testExecuteLog){
        for (TestExecuteLog value : LOG_MAP.values()) {
            if (value.equals(testExecuteLog)){
                return true;
            }
        }
        return false;
    }

    public static TestExecuteLog getCurrentTestExecute(Long threadId){
        return LOG_MAP.get(threadId);
    }

}
