package net.northking.cctp.debuger.websocket;

import com.alibaba.fastjson.JSON;
import com.hzbank.testteam.autotest.dependencies.authDependency.constants.AuthDependencyConstants;
import com.hzbank.testteam.autotest.dependencies.authDependency.dto.TokenValueDTO;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.utils.SpringUtil;
import net.northking.cctp.debuger.bean.DebugerConst;
import net.northking.cctp.debuger.bean.DebugerRequest;
import net.northking.cctp.debuger.listener.WebClientListener;
import net.northking.cctp.debuger.service.MobileScriptDebugService;
import net.northking.cctp.debuger.util.JsonUtils;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint("/pub/debug")
public class WSSessionManager implements ApplicationContextAware {
    private static final Logger logger = LoggerFactory.getLogger(WSSessionManager.class);

    private static Map<String,String> sessionKeyMap = new ConcurrentHashMap<>();

    private static Map<String, WebClientListener> listenerMap = new ConcurrentHashMap<>();


    private static ApplicationContext context;

    @PostConstruct
    public void init(){
        logger.info("Init web session manager ...");
    }


    /**
     * 与界面建立Websocket会话
     * @param session 会话对象
     */
    @OnOpen
    public  void onOpen(Session session){
        String queryString  = session.getQueryString();
        Map<String,String> queryParams = decodeQueryString(queryString);
        logger.info("收到页面建立连接请求:[queryString:{},localSessionId:{}]",queryString,session.getId());
        String token = queryParams.get(DebugerConst.AUTHORIZATION);
        String sessionId = queryParams.get(DebugerConst.KEY_SESSIONID);
        if(StringUtils.isBlank(token) || (!authorize(session.getId(),token))){
            closeSession(session, "用户认证失败");
            return;
        }
        if (StringUtils.isBlank(sessionId)){
            closeSession(session, "sessionId不能为空");
            return;
        }
        session.setMaxTextMessageBufferSize(10 * 1024 *1024);
        session.setMaxBinaryMessageBufferSize(10 * 1024 *1024);
        sessionKeyMap.put(session.getId(),sessionId);
        //监控ping的心跳
        WebClientListener listener = new WebClientListener(session, sessionId);
        listener.start();
        listenerMap.put(sessionId, listener);
        context.getBean(MobileScriptDebugService.class).addDebugSession(sessionId,session);
        logger.info("成功建立会话[sessionId:{}]",sessionId);
    }

    private void closeSession(Session session, String reasonMsg) {
        try {
            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, reasonMsg));
        } catch (IOException e) {
            logger.warn("Error when close session", e);
        }
    }

    /**
     * 根据token认证用户信息
     * @param token
     * @param sessionId
     * @return
     */
    private boolean authorize(String sessionId,String token){
        RedissonClient redisson = SpringUtil.getBean(RedissonClient.class);
        RBucket<TokenValueDTO> tokenValueDTORBucket = redisson.getBucket(AuthDependencyConstants.TOKEN_PREFIX + token);
        TokenValueDTO tokenValueDTO = tokenValueDTORBucket.get();
        if (tokenValueDTO == null ) {
            return false;
        }
        context.getBean(MobileScriptDebugService.class).addUserToken(sessionId, token);
        return true;
    }

    private Map<String,String> decodeQueryString(String queryString){
        Map<String,String> queryParams = new HashMap<>();
        if (StringUtils.isNotBlank(queryString)){
           String[] queryParamStrs = StringUtils.split(queryString,"[&]");
           if (queryParams!=null && queryParamStrs.length>0){
               for (String paramStr: queryParamStrs) {
                  String[] param = StringUtils.split(paramStr,"=");
                  queryParams.put(param[0],param[1]);
               }
           }
        }
        return queryParams;
    }

    /**
     * 关闭调试会话
     * @param session
     */
    @OnClose
    public void onClose(Session session,CloseReason closeReason){
        String sessionId =  sessionKeyMap.remove(session.getId());
        logger.info("收到页面关闭连接请求:[sessionId:{},localSessionId:{}]",sessionId,session.getId());
        if (null != closeReason) {
            logger.info("关闭代码：{}",closeReason.getCloseCode().getCode());
            logger.info("关闭原因：{}",closeReason.getReasonPhrase());
            logger.info("关闭原因：{}", JSON.toJSONString(closeReason));
        }
        context.getBean(MobileScriptDebugService.class).removeUserToken(session.getId());
        logger.info("关闭引擎连接:[sessionId:{}]",sessionId);
        context.getBean(MobileScriptDebugService.class).removeDebugSession(sessionId,session);
        //todo：有重连的机制
        logger.info("发出退出设备指令。。。。。。。。");
        context.getBean(MobileScriptDebugService.class).exitDeviceUsage(sessionId);
        logger.info("成功关闭调试会话[sessionId:{}]",sessionId);
        WebClientListener listener = listenerMap.remove(sessionId);
        listener.exitWebWS();
    }

    /**
     * 接收页面消息
     * @param msg
     * @param session
     */
    @OnMessage
    public  void onMessage(String msg,Session session){
        String sessionId =  sessionKeyMap.get(session.getId());
        logger.info("收到页面消息请求:[msg:{},sessionId:{},localSessionId:{}]",msg,sessionId,session.getId());
        if("ping".equalsIgnoreCase(msg)){
            logger.info("Received a ping msg");
            try{
                session.getBasicRemote().sendText("pong");
                WebClientListener listener = listenerMap.get(sessionId);
                listener.setPrePingTime(System.currentTimeMillis());
            }catch (Exception e){
                logger.warn("Error when response ping msg",e);
            }
            return;
        }
        DebugerRequest request = JsonUtils.fromJsonString(msg, DebugerRequest.class);
        String token = context.getBean(MobileScriptDebugService.class).getUserToken(session.getId());
        request.setUserToken(token);
        context.getBean(MobileScriptDebugService.class).processWebMsg(JsonUtils.toJson(request));
    }
    @OnError
    public  void onError(Session session,Throwable t){
        logger.error(String.format("Session Error->sessionId:%s",session.getId()),t);
        onClose(session,new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY,"错误出现关闭"));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}
