package com.ruoyi.imserver.netty.processor;

import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.model.AppLoginUser;
import com.ruoyi.common.im.constant.IMConstant;
import com.ruoyi.common.im.constant.IMRedisKey;
import com.ruoyi.common.im.enums.IMCmdType;
import com.ruoyi.common.im.model.IMLoginInfo;
import com.ruoyi.common.im.model.IMSendInfo;
import com.ruoyi.common.im.model.IMSessionInfo;
import com.ruoyi.common.im.mq.RedisMQTemplate;
import com.ruoyi.common.utils.JwtUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.imserver.constant.ChannelAttrKey;
import com.ruoyi.imserver.netty.IMServerGroup;
import com.ruoyi.imserver.netty.UserChannelCtxMap;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class LoginProcessor extends AbstractMessageProcessor<IMLoginInfo> {

    private final RedisMQTemplate redisMQTemplate;
    @Value("${token.secret}")
    private String accessTokenSecret;

    @Override
    public  void process(ChannelHandlerContext ctx, IMLoginInfo loginInfo) {
        if (!checkSign(loginInfo.getAccessToken())) {
            ctx.channel().close();
            log.warn("用户token校验不通过，强制下线,token:{}", loginInfo.getAccessToken());
            return;
        }
        Claims claims= getClaims(loginInfo.getAccessToken());
        Long userId= claims.get("userid",Long.class);
//        String strInfo = JwtUtil.getInfo(loginInfo.getAccessToken());
//        IMSessionInfo sessionInfo = JSON.parseObject(strInfo, IMSessionInfo.class);
//        Long userId= SecurityUtils.getLoginUser().getUser().getUserId();//todo 获取用户信息有误
        Integer terminal = 1;
        log.info("用户登录，userId:{}", userId);
        ChannelHandlerContext context = UserChannelCtxMap.getChannelCtx(userId, terminal);
        if (context != null && !ctx.channel().id().equals(context.channel().id())) {
            // 不允许多地登录,强制下线
            IMSendInfo<Object> sendInfo = new IMSendInfo<>();
            sendInfo.setCmd(IMCmdType.FORCE_LOGUT.code());
            sendInfo.setData("您已在其他地方登录，将被强制下线");
            context.channel().writeAndFlush(sendInfo);
            log.info("异地登录，强制下线,userId:{}", userId);
        }
        // 绑定用户和channel
        UserChannelCtxMap.addChannelCtx(userId, terminal, ctx);
        // 设置用户id属性
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        ctx.channel().attr(userIdAttr).set(userId);
        // 设置用户终端类型
        AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
        ctx.channel().attr(terminalAttr).set(terminal);
        // 初始化心跳次数
        AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf(ChannelAttrKey.HEARTBEAT_TIMES);
        ctx.channel().attr(heartBeatAttr).set(0L);
        // 在redis上记录每个user的channelId，15秒没有心跳，则自动过期
        String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString());
        redisMQTemplate.opsForValue().set(key, IMServerGroup.serverId, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
        // 响应ws
        IMSendInfo<Object> sendInfo = new IMSendInfo<>();
        sendInfo.setCmd(IMCmdType.LOGIN.code());
        ctx.channel().writeAndFlush(sendInfo);
    }


    @Override
    public IMLoginInfo transForm(Object o) {
        HashMap map = (HashMap) o;
        return BeanUtil.fillBeanWithMap(map, new IMLoginInfo(), false);
    }

    private boolean checkSign(String accessToken){
        try {
            // 关键步骤：解析Token并自动验证签名（使用相同的SECRET）
            Jwts.parser()
                    .setSigningKey(accessTokenSecret)
                    .parseClaimsJws(accessToken)
                    .getBody();
            return true;
        } catch (JwtException e) {
            return false;
        }

    }

    private Claims getClaims(String accessToken){
        try {
            // 关键步骤：解析Token并自动验证签名（使用相同的SECRET）
            return Jwts.parser()
                    .setSigningKey(accessTokenSecret)
                    .parseClaimsJws(accessToken)
                    .getBody();

        } catch (JwtException e) {
        }
        return null;
    }
}
