package net.northking.cctp.se.debug.service.impl;

import com.alibaba.fastjson.JSON;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import net.northking.cctp.common.http.ResultWrapper;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.se.config.AtuServerConfig;
import net.northking.cctp.se.debug.bean.DebugerConst;
import net.northking.cctp.se.debug.bean.DebuggerRequest;
import net.northking.cctp.se.debug.bean.DebuggerResponse;
import net.northking.cctp.se.debug.service.DebugService;
import net.northking.cctp.se.debug.service.DebugSession;
import net.northking.cctp.se.debug.service.DefaultDebugSession;
import net.northking.cctp.se.thread.ExecutorPool;
import net.northking.cctp.se.util.JsonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.logging.LogLevel;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.websocket.CloseReason;
import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;

@Slf4j
@Service
public class DebugServiceImpl implements DebugService {

    @Autowired
    private AtuServerConfig atuServerConfig;

    private Map<String, DebugSession> wsSessionMap = new ConcurrentHashMap<>(); // 好像不应该放这里

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public DebugSession createDebugSession(String deviceToken,String sessionType, Session session, String sessionId) {
        try {
            DebugSession debugSession = new DefaultDebugSession(deviceToken,sessionType,session, sessionId);
            if (TYPE_PC.equalsIgnoreCase(sessionType)) {
                if (ExecutorPool.isPCAvailable()) {
                    // pc有且只能有一个session。不允许创建新的pc session
                    wsSessionMap.put(sessionId, debugSession);
                    Future<?> submit = ExecutorPool.getPCPool().submit(debugSession);
                    debugSession.setFuture(submit);
                } else {
                    try {
                        DebuggerResponse.newResponse()
                                .sessionId(sessionId)
                                .finish(true).success(false)
                                .cmd(DebugerConst.ReplyConst.KEY_CMD_LOG).log("连接忙碌，尝试等待30秒进行连接", LogLevel.INFO.name(), "创建连接")
                                .send(session);
                        for (int i = 0; i < 30; i++) {
                            if (ExecutorPool.isPCAvailable()) {
                                // pc有且只能有一个session。不允许创建新的pc session
                                wsSessionMap.put(sessionId, debugSession);
                                Future<?> submit = ExecutorPool.getPCPool().submit(debugSession);
                                debugSession.setFuture(submit);
                            } else {
                                Thread.sleep(1000L);
                            }
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new ExecuteException("创建连接被打断");
                    }

                }
            }
            if (TYPE_MOBILE.equalsIgnoreCase(sessionType)) {
                if (ExecutorPool.isMobileAvailable()) {  // 活跃线程数 < 11
                    wsSessionMap.put(sessionId, debugSession);
                    Future<?> submit = ExecutorPool.getMobilePool().submit(debugSession);
                    debugSession.setFuture(submit);
                }
            }
            if (TYPE_INTERFACE.equalsIgnoreCase(sessionType)) {
                if (ExecutorPool.isApiAvailable()) {
                    wsSessionMap.put(sessionId, debugSession);
                    Future<?> submit = ExecutorPool.getApiPool().submit(debugSession);
                    debugSession.setFuture(submit);
                }
            }
            if (wsSessionMap.containsKey(sessionId)) {
                wsSessionMap.forEach((s1, d1) -> log.info("sessionId:{} -> token:{} -> type:{}", s1, d1.getDeviceToken(), d1.getSessionType()));
                return debugSession;
            }
        } catch (Exception e) {
            log.error("创建调试失败", e);
            throw e;
        }
        return null;
    }

    @Override
    public void destroyDebugSession(String sessionId) {
        DebugSession debugSession = wsSessionMap.get(sessionId);
        if(debugSession!=null){
            Lock lock = debugSession.getLock();
            lock.lock();
            try {
                wsSessionMap.remove(sessionId);
                log.info("one session has been destroyed:{}", JSON.toJSONString(debugSession));
                debugSession.destroy();
                log.debug("session存活数量：{}", wsSessionMap.size());
                wsSessionMap.forEach((s1, d1) -> log.debug("sessionId:{} -> token:{} -> type:{}", s1, d1.getDeviceToken(), d1.getSessionType()));
            }catch (Exception e){
                throw e;
            }finally {
                lock.unlock();
            }
        }
    }

    @Override
    public void receiveRequestMsg(Session session,String requestMsg) {
       DebuggerRequest request = JsonUtils.fromJsonString(requestMsg, DebuggerRequest.class);
       if (null == request || StringUtils.isBlank(request.getSessionId())){
           log.warn("Invalid request msg:{}",requestMsg);
           // todo: 返回前端失败日志
           return;
       }
       DebugSession debugSession = wsSessionMap.get(request.getSessionId());
       if (null == debugSession){
           log.warn("Session[{}] not found",request.getSessionId());
           String msg = String.format("Session[%s] 已关闭，请重新发起连接", request.getSessionId());
           try {
               DebuggerResponse.newResponse()
                       .sessionId(request.getSessionId())
                       .finish(true).success(false)
                       .cmd(DebugerConst.ReplyConst.KEY_CMD_LOG).log(msg, LogLevel.ERROR.name(), "连接失败")
                       .send(session);
               DebuggerResponse.newResponse()
                       .sessionId(request.getSessionId())
                       .finish(true)
                       .cmd(DebugerConst.ReplyConst.KEY_CMD_REPLY)
                       .msg(msg)
                       .success(false).send(session);
               session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "通道异常关闭，手动关闭通道:sit"));
               log.info(msg);
               return;
           } catch (IOException e) {
               throw new ExecuteException(msg, e);
           }
       }
        Object type = request.getData().get("type");
       boolean mobileFlag = false;
        if (type != null) {
            // 0-接口 1- b/s 2 c/s 3 安卓 4 ios
            mobileFlag = "3".equalsIgnoreCase(type + "") || "4".equalsIgnoreCase(type + "");
        }
        if (mobileFlag && DebugerConst.CommandConst.CMD_DEVICE_INIT.equalsIgnoreCase(request.getCmd())) {  //设置应用下载的url
            Map<String, Object> data = request.getData();
            String appId = (String) data.get("appId");
            String appDownloadUrl = atuServerConfig.getAppDownloadUrl().replace("{appId}", appId);
            data.put("appDownloadUrl", atuServerConfig.getServerUrl() + appDownloadUrl);
        }
        // 处理replay_stop
        if (DebugerConst.CommandConst.CMD_REPLAY_STOP.equalsIgnoreCase(request.getCmd())) {
            debugSession.stop(request);
            return;
        }
       debugSession.processRequest(request);
    }

    /**
     * 获取调试会话
     *
     * @param sessionId
     * @return
     */
    @Override
    public DebugSession getDebugSession(String sessionId) {
        return this.wsSessionMap.get(sessionId);
    }

    /**
     * 检查并清除失效的连接
     *
     * @param type 类型
     */
    @Override
    public void checkAndClear(String type) {
        // TODO: 本地录制不需要
        try {
            wsSessionMap.forEach((s, d) -> {
                if (!TYPE_INTERFACE.equalsIgnoreCase(type) && type.equalsIgnoreCase(d.getSessionType())) {  // 只检查非接口类型的调试
                    // 是否有效
                    String deviceInfoUrl = String.format("%s%s/%s", atuServerConfig.getServerUrl(), atuServerConfig.getDeviceInfoPath(), d.getDeviceToken());
                    ResponseEntity<ResultWrapper> resEntity = restTemplate.getForEntity(deviceInfoUrl, ResultWrapper.class);
                    ResultWrapper wrapper = resEntity.getBody();
                    if (wrapper != null && wrapper.isSuccess() && wrapper.getData() != null) {
                        if (((Map) wrapper.getData()).isEmpty()) {// token查询不了设备信息，也就是token已经失效了
                            log.debug("sessionId【{}】，toke【{}】失效，进行清理操作..", s, d.getDeviceToken());
                            destroyDebugSession(s);
                        }
                    }
                }
            });
        } catch (Exception e) {
            log.error("清理失效连接失败", e);
        }
    }


}
