package com.wecloud.im.tillo.netty.handler;


import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.wecloud.im.tillo.app_ws.WsHandler;
import com.wecloud.im.tillo.app_ws.model.WsConstants;
import com.wecloud.im.tillo.app_ws.service.MangerChannelService;
import com.wecloud.im.tillo.app_ws.utils.FullHttpRequestUtils;
import io.geekidea.springbootplus.config.constant.CommonConstant;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;

/**
 * @Description 聊天模块 http请求处理
 * @Author hewei hwei1233@163.com
 * @Date 2019-07-19
 */
@Component
@Slf4j
public class NettyApiRequest {

    @Autowired
    private ShiroLoginService shiroLoginService;

    @Resource
    private MangerChannelService appUserChannelsService;

    @Resource
    private WsHandler appImHandler;

    /**
     * http请求接收
     *
     * @param ctx
     * @param msg
     * @param httpRequest
     * @throws Exception
     */
    public void handle(ChannelHandlerContext ctx, Object msg, FullHttpRequest httpRequest) throws Exception {
        if (!(msg instanceof FullHttpRequest)) {
//                            String context = JsonUtil.obj2Json(ResultModel.error(ResultStatus.REQUEST_ERROR));
            String context = "JsonUtil.obj2Json(ResultModel.error(ResultStatus.REQUEST_ERROR))";
            FullHttpRequestUtils.send(ctx, context, HttpResponseStatus.OK);
            return;
        }
        String path = httpRequest.uri();
        String body = FullHttpRequestUtils.getBody(httpRequest);

        if (log.isDebugEnabled()) {
            log.debug("httpRequest:\n" + httpRequest.toString() + "\n" + body);
        }

        if (path.contains(WsConstants.WS_URL)) {
            /*
              app聊天http升级webSocket
             */
            this.initWs(ctx, httpRequest);

        }
    }

    /**
     * 初始化websocket
     */
    private void initWs(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {
        Map<String, String> paramMap = FullHttpRequestUtils.parameterParse(httpRequest);
        String token = paramMap.get(WsConstants.TOKEN);
//        String deviceId = paramMap.get(RequestHeaderConstants.DEVICE_ID);

        DecodedJWT jwtInfo = JwtUtil.getJwtInfo(token);
        String payload = jwtInfo.getPayload();
        Base64.Decoder decoder = Base64.getDecoder();
        payload = new String(decoder.decode(payload), StandardCharsets.UTF_8);
        JSONObject jsonObject = JSONObject.parseObject(payload);
        String appKey = (String) jsonObject.get("appKey");
        String clientId = (String) jsonObject.get(CommonConstant.CLIENT_ID);

        if (StringUtils.isBlank(token)) {
            throw new AuthenticationException("token不能为空");
        }
        if (JwtUtil.isExpired(token)) {
            throw new AuthenticationException("JWT Token已过期,token:" + token);
        }

        // 验签token
        JwtToken jwtToken = shiroLoginService.getTJwtTokenForRedis(token);

        if (jwtToken == null) {
            log.error("jwtToken == null ,token和redis不一致, clientId:" + clientId + ",token:" + token);
            return;
        }

        if ((!jwtToken.getClientId().equals(clientId)) || (!jwtToken.getAppKey().equals(appKey))) {
            log.error("clientId  appKey 不一致");
            return;
        }

        // 设置uri前缀
        httpRequest.setUri(WsConstants.WS_URL);

        // 保持当前连接
        ctx.fireChannelRead(httpRequest.retain());

        // 设置属性值 userid - channel
        ctx.channel().attr(MangerChannelService.CLIENT_ID).set(clientId);
        ctx.channel().attr(MangerChannelService.APP_KEY).set(appKey);

//         添加长连接handler
        ctx.pipeline().addLast("appImHandler", appImHandler);

        // 保存用户上下文对象
        appUserChannelsService.put(appKey, clientId, (NioSocketChannel) ctx.channel());

        //移除当前api处理handler， 不再参与长连接处理
        ctx.pipeline().remove("SingleHttpRequestHandler");

    }


}
