package com.ym.im.handler;

import com.ym.im.entity.JwtTokenRedisVo;
import com.ym.im.entity.MsgBody;
import com.ym.im.entity.base.BaseSocketInfo;
import com.ym.im.entity.base.ChannelAttributeKey;
import com.ym.im.entity.base.NettyConstant;
import com.ym.im.entity.enums.RoleEnum;
import com.ym.im.exception.HttpException;
import com.ym.im.factory.SingleChatFactory;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.Optional;

import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;


/**
 * @author: JJww
 * @Date:2020/10/10
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketHandshakerHandler extends BaseHandler<FullHttpRequest> {

    @Autowired
    private ChannelGroupHandler channelGroup;

    @Autowired
    private SingleChatFactory singleChatFactory;

    @Resource(name = "myRedisTemplate")
    private RedisTemplate redisTemplate;

    public static final String AUTHORIZATION = "Authorization";

    /**
     * 登录用户token信息key
     * login:token:tokenMd5
     */
    public static final String LOGIN_TOKEN = "login:token:%s";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {

        final String token = fullHttpRequest.headers().get(AUTHORIZATION);
        Optional.ofNullable(token).orElseThrow(() -> new HttpException(ctx, fullHttpRequest, new DefaultFullHttpResponse(HTTP_1_1, UNAUTHORIZED)));
        final JwtTokenRedisVo tokenInfoForRedis = this.getTokenInfoForRedis(token);
        Optional.ofNullable(tokenInfoForRedis).orElseThrow(() -> new HttpException(ctx, fullHttpRequest, new DefaultFullHttpResponse(HTTP_1_1, UNAUTHORIZED)));
        final String roleType = tokenInfoForRedis.getType();
        if (RoleEnum.merchant.getType().equals(roleType)) {
            final Long merchantId = tokenInfoForRedis.getMcId();
            Optional.ofNullable(merchantId).orElseThrow(() -> new HttpException(ctx, fullHttpRequest, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST)));
            ctx.channel().attr(ChannelAttributeKey.MERCHANT_ID).set(merchantId);
        }
        final Long userId = tokenInfoForRedis.getUserId();
        ctx.channel().attr(ChannelAttributeKey.ROLE_ID).set(userId);
        ctx.channel().attr(ChannelAttributeKey.TOKEN_INFO).set(token);
        ctx.channel().attr(ChannelAttributeKey.ROLE_TYPE).set(roleType);
        this.sso(token, userId, roleType);
        singleChatFactory.getService(roleType).init(ctx);
        fullHttpRequest.setUri(NettyConstant.CS);
        ctx.fireChannelRead(fullHttpRequest.retain());
    }


    private void sso(String token, Long roleId, String type) {
        BaseSocketInfo baseSocketInfo = null;
        switch (RoleEnum.get(type)) {
            case APP:
                baseSocketInfo = channelGroup.USER_GROUP.get(roleId);
                channelGroup.USER_GROUP.remove(roleId);
                break;
            case merchant:
                baseSocketInfo = channelGroup.getMerchantStaff(roleId);
                channelGroup.removeMerchantStaff(roleId);
                break;
            default:
        }
        if (baseSocketInfo != null && !token.equals(baseSocketInfo.getToken())) {
            baseSocketInfo.writeAndFlush(new MsgBody<>().setCode(MsgBody.FORCEDOFFLINE));
            baseSocketInfo.close();
            log.info("用户: " + roleId + " 被迫下线");
        }
    }

    public JwtTokenRedisVo getTokenInfoForRedis(String token) {
        final LinkedHashMap jwtTokenInfo = (LinkedHashMap) redisTemplate.opsForValue().get(String.format(LOGIN_TOKEN, DigestUtils.md5Hex(token)));
        if (jwtTokenInfo == null) {
            return null;
        }
        //@class 对象路径不一致 重新set
        final String type = jwtTokenInfo.get("type").toString();
        return new JwtTokenRedisVo()
                .setUserId(Long.valueOf(jwtTokenInfo.get("userId").toString()))
                .setType(type).setMcId(RoleEnum.merchant.getType().equals(type) ? Long.valueOf(jwtTokenInfo.get("mcId").toString()) : null);

    }

}
