package org.matrix.socket;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.matrix.actuators.datasource.IDataSourceService;
import org.matrix.actuators.httpclient.HttpResponseDetail;
import org.matrix.actuators.sql.SqlExpActuator;
import org.matrix.actuators.usecase.CaseActuator;
import org.matrix.actuators.usecase.RunCaseResult;
import org.matrix.actuators.usecase.TestCaseReport;
import org.matrix.actuators.usecase.TestDataExecuteResult;
import org.matrix.database.entity.*;
import org.matrix.database.service.IExecutionHistoryService;
import org.matrix.database.service.ITestCaseService;
import org.matrix.database.service.ITestDataService;
import org.matrix.database.service.ITestJobService;
import org.matrix.exception.GlobalException;
import org.matrix.socket.enums.SocketType;
import org.matrix.socket.pool.MonitorSocketPool;
import org.matrix.socket.vo.CaseExecuteVo;
import org.matrix.socket.vo.ExecuteMonitorVo;
import org.matrix.socket.vo.SocketResponseMessage;
import org.matrix.socket.vo.SocketVo;
import org.matrix.testNg.web.entity.DataBean;
import org.matrix.testNg.web.entity.DataBeans;
import org.matrix.testNg.web.entity.DataMove;
import org.matrix.testNg.web.entity.ReportMessage;
import org.matrix.testNg.web.report.TestNgImpl;
import org.matrix.testNg.web.vo.DataBeansJobVo;
import org.matrix.testNg.web.vo.DataBeansMoveVo;
import org.matrix.util.JsonUtil;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * webSocket处理 调用和执行的日志发送
 *
 * @author huangxiahao
 */
@Component
public class ExecutionSocketHandler extends TextWebSocketHandler {



    public static final MonitorSocketPool EXECUTE_MONITOR_SOCKET_POOL = new MonitorSocketPool();

    final CaseActuator caseActuator;
    final ITestDataService testDataService;
    final ITestJobService testJobService;
    final ITestCaseService testCaseService;
    final SqlExpActuator sqlExpActuator;
    final JdbcTemplate jdbcTemplate;
    final IDataSourceService dataSourceService;
    final IExecutionHistoryService executionHistoryService;
    final TestNgImpl testNg;

    public ExecutionSocketHandler(CaseActuator caseActuator, ITestDataService testDataService, ITestJobService testJobService, ITestCaseService testCaseService, SqlExpActuator sqlExpActuator, JdbcTemplate jdbcTemplate, IDataSourceService dataSourceService, TestNgImpl testNg, IExecutionHistoryService executionHistoryService) {
        this.caseActuator = caseActuator;
        this.testDataService = testDataService;
        this.testJobService = testJobService;
        this.testCaseService = testCaseService;
        this.sqlExpActuator = sqlExpActuator;
        this.jdbcTemplate = jdbcTemplate;
        this.dataSourceService = dataSourceService;
        this.testNg = testNg;
        this.executionHistoryService = executionHistoryService;
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        //检验客户端是否进行测试用例调试
        try {
            SocketVo socketVo = JSON.parseObject(payload, SocketVo.class);
            //如果客户端发送测试用例调试命令，则进行测试用例的执行
            if (socketVo != null && socketVo.getSocketType() != null) {
                if (SocketType.TEST_CASE_EXECUTE.equals(socketVo.getSocketType())) {
                    CaseExecuteVo caseExecuteVo = JSON.parseObject(payload, CaseExecuteVo.class);
                    caseActuator.runTestCase(session, caseExecuteVo);
                } else if (SocketType.TEST_JOB_EXECUTE.equals(socketVo.getSocketType())) {
                    CaseExecuteVo caseExecuteVo = JSON.parseObject(payload, CaseExecuteVo.class);
                    TestJob byId = testJobService.getById(caseExecuteVo.getJobId());
                    if (byId==null){
                        throw new GlobalException(String.format("执行错误，测试任务Id：%s 不存在",caseExecuteVo.getJobId()));
                    }
                    String sqlStatement = byId.getSqlStatement();
                    dataSourceService.switchMainDataSource();
                    List<Map<String, Object>> list = jdbcTemplate.queryForList(sqlStatement);
                    for (Map<String, Object> objectMap : list) {
                        TestCase testCase = BeanUtil.mapToBean(objectMap, TestCase.class, false);
                        List<TestData> testDataList = testDataService.list(Wrappers.lambdaQuery(TestData.class).eq(TestData::getTestCaseId, testCase.getId()));
                        if (testDataList.size()==0){
                            throw new GlobalException(String.format("执行错误，测试任务ID：%s    用例Id：%s  用例名：%s  \r\n 该用例中不存在需要测试的数据组",caseExecuteVo.getJobId(),testCase.getId(),testCase.getName()));
                        }
                        TestCaseListDataBto testCaseListDataBto = new TestCaseListDataBto();
                        testCaseListDataBto.setTestCase(testCase);
                        testCaseListDataBto.setTestDataList(testDataList);
                        //检查一下这些case里有没有东西，如果没有东西就不执行了
                        caseExecuteVo.getTestCaseListDataBtoList().add(testCaseListDataBto);
                    }

                    if (caseExecuteVo.getTestCaseListDataBtoList().size() > 0) {
                        caseExecuteVo.setProjectId(caseExecuteVo.getTestCaseListDataBtoList().get(0).getTestCase().getProjectId());
                    }
                    RunCaseResult runCaseResult = caseActuator.runTestCase(session, caseExecuteVo);
                    ReportMessage reportMessage = testNg.getReporterUrls(initTestNgData(runCaseResult, caseExecuteVo));
                    ExecutionHistory history = new ExecutionHistory();
                    history.setUrl(reportMessage.getUrl());
                    history.setStatus(null);
                    history.setUpdateTime(null);
                    executionHistoryService
                            .update(history,Wrappers.lambdaUpdate(ExecutionHistory.class)
                                    .eq(ExecutionHistory::getUniqueKey,caseExecuteVo.getUniqueKey()));
                } else if (SocketType.TEST_CASE_MONITOR.equals(socketVo.getSocketType())) {
                    ExecuteMonitorVo caseExecuteVo = JSON.parseObject(payload, ExecuteMonitorVo.class);
                    EXECUTE_MONITOR_SOCKET_POOL.add(caseExecuteVo.getUniqueKey(), session);
                } else {
                    throw new GlobalException("入参不符合规定");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            session.sendMessage(new TextMessage(SocketResponseMessage.fail(e.getMessage())));
        }
    }

    public DataBeansJobVo initTestNgData(RunCaseResult caseResult, CaseExecuteVo caseExecuteVo) {
        //用例总数
        Integer total = caseExecuteVo.getTestCaseListDataBtoList().size();
        //通过用例数
        Integer passNum = (int) caseResult.getCaseReports().stream().filter(TestCaseReport::getStatus).count();
        //失败用例数
        Integer failNum = total - passNum;
        //执行总时间
        String allTime = caseResult.getDurationTimeStr();
        //用例通过率
        Integer probability;
        if (total==0L){
            probability = 0;
        }else {
            probability =  BigDecimal.valueOf(passNum).divide(BigDecimal.valueOf(total), 2, RoundingMode.UP).multiply(BigDecimal.valueOf(100)).intValue();
        }
        DataBean dataBean = new DataBean(total, passNum, failNum, allTime, probability);
        List<DataBeansMoveVo> dataBeansMoveVos = new ArrayList<>();
        for (TestCaseReport caseReport : caseResult.getCaseReports()) {
            DataBeansMoveVo dataBeansMoveVo = new DataBeansMoveVo();
            dataBeansMoveVo.setDataMove(new DataMove(
                    caseReport.getMoveBefore(),
                    caseReport.getMoveAfterTest(),
                    caseReport.getMoveAfterCase()
            ));
            List<DataBeans> dataBeansList = new ArrayList<>();
            for (TestDataExecuteResult dataExecuteResult : caseReport.getDataExecuteResults()) {
                DataBeans dataBeans = new DataBeans();
                dataBeans.setTestCaseName(caseReport.getCaseName());
                dataBeans.setType(caseReport.getType());
                if (dataExecuteResult.getBaseTestCaseResponseDetail() != null){
                    if (dataExecuteResult.getBaseTestCaseResponseDetail() instanceof HttpResponseDetail){
                        HttpResponseDetail httpResponseDetail = (HttpResponseDetail)dataExecuteResult.getBaseTestCaseResponseDetail();
                        dataBeans.setDetail(JsonUtil.toJsonPretty(JSONObject.parse(httpResponseDetail.getResponseBody())));
                    }
                }
                dataBeans.setResult(dataExecuteResult.getExecutionStatus());
                dataBeans.setDescription(caseReport.getDescription());
                dataBeans.setDuration(dataExecuteResult.getDurationTimeMillis());
                dataBeans.setResultMessage(dataExecuteResult.getCheckPointResult());
                dataBeansList.add(dataBeans);
            }
            dataBeansMoveVo.setDataBeansList(dataBeansList);
            dataBeansMoveVos.add(dataBeansMoveVo);
        }
        return new DataBeansJobVo(dataBean,dataBeansMoveVos);
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        EXECUTE_MONITOR_SOCKET_POOL.remove(session);
    }


}
