package net.northking.cctp.se.debugger.endpoint;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.se.debugger.pool.WebSocketInterfaceDebuggerPool;
import net.northking.cctp.se.dto.ATUWebSocketMessage;
import net.northking.cctp.se.dto.ATUWebSocketReplay;
import net.northking.cctp.se.script.ATUScript;
import net.northking.cctp.se.util.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;

import static javax.websocket.CloseReason.CloseCodes.CANNOT_ACCEPT;

/**
 * 接口自动化测试调试器 WebSocket服务端
 */
@Component
public class DebuggerInterfaceWebSocketServer
{
  private static final Logger logger = LoggerFactory.getLogger(DebuggerInterfaceWebSocketServer.class);

  /**
   * JSON 序列化工具
   */
  private static ObjectMapper objectMapper = new ObjectMapper();

  @OnOpen
  public void onOpen(
      Session session,
      @PathParam("deviceToken") String deviceToken)
  {
    logger.info("建立连接：token="+deviceToken);
    WebSocketInterfaceDebuggerPool.getInstance().createDebugger(session, deviceToken);
  }

  @OnClose
  public void onClose(@PathParam("deviceToken") String deviceToken)
  {
    WebSocketInterfaceDebuggerPool.getInstance().destroyDebugger(deviceToken);
  }

  /**
   * 键名： 指令类型
   */
  public static final String KEY_CMD_TYPE = "cmdType";

  /**
   * 命令： 开始回放
   */
  public static final String CMD_REPLAY_START = "replay_start";

  /**
   * 命令： 停止回放
   */
  public static final String CMD_REPLAY_STOP = "replay_stop";

  /**
   * 命令： 接口定义调试
   */
  public static final String CMD_DEBUG_API = "debug_api";

  @OnMessage
  public void onMessage(
      Session session,
      @PathParam("deviceToken") String deviceToken,
      String text) throws JsonProcessingException
  {
    logger.info("收到消息："+text);
    Map<String, Object> requestMap = objectMapper.readValue(text, Map.class);

    String cmdType = requestMap.get(KEY_CMD_TYPE).toString();
    String data = requestMap.get("data").toString();
    if(CMD_DEBUG_API.equalsIgnoreCase(cmdType)){
      //仿真客户端-接口定义调试
      debugStart(session,deviceToken, text);

    }else if (CMD_REPLAY_START.equalsIgnoreCase(cmdType))
    {// 开始回放
      replayStart(deviceToken, text);
    } else if (CMD_REPLAY_STOP.equalsIgnoreCase(cmdType))
    {// 停止回放
      replayStop(deviceToken);
    }

  }

  protected void debugStart(Session session,String deviceToken, String text) throws JsonProcessingException
  {
    //json格式化数据
    TypeReference<ATUWebSocketMessage<ATUWebSocketReplay>> typeReference = new TypeReference<ATUWebSocketMessage<ATUWebSocketReplay>>()
    {
    };
    ATUWebSocketMessage<ATUWebSocketReplay> message = objectMapper.readValue(text, typeReference);
    ATUWebSocketReplay data = message.getData();
    //接口调试获取脚本资源
    String stepId = data.getStepId();
    if(StringUtils.hasText(stepId)){
      //根据地址获取脚本文件并解析脚本文件成脚本对象
      String content = null;
      try {
        String[] result = idToPath(stepId);
        content = getFileContent(result[0], result[1]);
      } catch (Exception e) {
        logger.error("接口脚本信息获取异常",e);
        try {
          session.close(new CloseReason(CANNOT_ACCEPT,"接口脚本信息获取失败"));
        } catch (IOException e1) {
          logger.error("socket连接关闭异常:",e1);
        }
      }

      ATUScript atuScript = JSON.parseObject(content, ATUScript.class);

      //传入脚本信息
      data.setScript(atuScript);
      WebSocketInterfaceDebuggerPool.getInstance().replayStart(deviceToken, data);
    }else{
      //返回消息 无脚本可执行
      logger.warn("接口无可用脚本信息请检查 接口配置");
      try {
        session.close(new CloseReason(CANNOT_ACCEPT,"接口无可用脚本信息请检查接口配置"));
      } catch (IOException e) {
        logger.error("接口调试异常:",e);
      }
    }
  }

  protected void replayStart(String deviceToken, String text) throws JsonProcessingException
  {
    TypeReference<ATUWebSocketMessage<ATUWebSocketReplay>> typeReference = new TypeReference<ATUWebSocketMessage<ATUWebSocketReplay>>()
    {
    };
    ATUWebSocketMessage<ATUWebSocketReplay> message = objectMapper.readValue(text, typeReference);
    WebSocketInterfaceDebuggerPool.getInstance().replayStart(deviceToken, message.getData());

  }

  protected void replayStop(String deviceToken)
  {
    WebSocketInterfaceDebuggerPool.getInstance().replayStop(deviceToken);
  }


  @OnError
  public void onError(
      Session session,
      @PathParam("debuggerId") String debuggerId,
      Throwable error
  )
  {

    logger.error("OnError: 发生异常！【接口自动化测试调试器】= " + debuggerId, error);

  }

  private String getFileContent(String tenantId, String fileId) {
    String msg = null;
    if (null == fileId) {
      logger.debug("文件id不存在");
      return msg;
    }
    try {
      StringBuilder builder = new StringBuilder();
      // 修改成http下载 2023.3.1 jieying.li
      InputStream is = HttpUtils.download2Stream(tenantId + fileId);

      String line;
      BufferedReader read = new BufferedReader(new InputStreamReader(is));
      while((line = read.readLine())!=null){
        builder.append(line);
      }
      msg = builder.toString();
    } catch (Exception e) {
      throw new ExecuteException(String.format("文件下载失败租户id: %s，错误信息：%s", tenantId, e.getMessage()), e);
    }
    return msg;
  }

  /**
   * 第一个是租户id,第二个是文件id，第三个是文件名
   * @param fileId
   * @return
   */
  private String[] idToPath(String fileId) {
    String[] result = new String[3];
    if (fileId.startsWith("/") || fileId.startsWith("_")) {
      fileId = fileId.substring(1);
    }
    if (fileId.endsWith("/") || fileId.endsWith("_")) {
      fileId = fileId.substring(0, fileId.length() - 1);
    }
    int index = fileId.indexOf("_");
    int lastIndex = fileId.lastIndexOf("_");
    if (index <= 0 || lastIndex <= 0 || index >= lastIndex) {
      logger.info("传入的id为：{}",fileId);
      throw new RuntimeException("脚本信息获取失败,错误的文件id:"+fileId);
    }
    result[0] = fileId.substring(0, index);  //租户id
    result[1] = fileId.substring(index + 1).replaceAll("_","/");  //文件id
    result[2] = fileId.substring(lastIndex + 1);  //文件名
    return result;
  }

}
