package com.cpop.core.filter;

import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.ip.IpUtils;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.entity.loginInfo.MiniUserLoginInfo;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.gateway.miniProgram.MiniProgramAuthenticationToken;
import com.cpop.core.gateway.sys.SysAuthenticationToken;
import com.cpop.core.service.CoreService;
import com.cpop.core.service.RedisService;
import com.cpop.core.service.impl.OamStaffDetailsServiceImpl;
import com.cpop.core.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.lang.NonNullApi;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author: DB
 * @date: 2022-09-25 16:34
 * @Description: jwt认证过滤器
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private OamStaffDetailsServiceImpl oamStaffDetailsService;

    @Autowired
    private CoreService coreService;

    @Autowired
    private RedisService redisService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response,@NonNull FilterChain chain) throws IOException, ServletException {
        String jwt = request.getHeader(jwtUtils.getHeader());
        // 这里如果没有jwt，继续往后走，因为后面还有鉴权管理器等去判断是否拥有身份凭证，所以是可以放行的
        // 没有jwt相当于匿名访问，若有一些接口是需要权限的，则不能访问这些接口
        if (StringUtils.isEmpty(jwt)) {
            chain.doFilter(request, response);
            return;
        }
        Jws<Claims> jws = jwtUtils.getClaimsByToken(jwt);
        if (jws == null) {
            returnJwtException(response,"token 失效");
            return;
        }
        Claims claim = jws.getBody();
        if (claim == null) {
            returnJwtException(response,"token 异常");
            return;
        }
        if (jwtUtils.isTokenExpired(claim)) {
            returnJwtException(response,"token 已过期");
            return;
        }
        //当前用户类型下唯一
        String identification = claim.getSubject();
        String username = jws.getHeader().get(Constants.USERNAME).toString();
        UserType userType = UserType.valueOf(jws.getHeader().get(Constants.USER_TYPE).toString());
        //从缓存中获取
        JSONObject jsonObject = redisService.getCacheObject(userType.getKey() + identification);
        LoginUser loginUser;
        if (null == jsonObject){
            loginUser = multipleLoadUser(userType, username);
            //存入缓存
            redisService.setCacheObject(userType.getKey() + loginUser.getIdentification(), loginUser);
        } else {
            loginUser = jsonObject.toJavaObject(LoginUser.class);
        }
        //获取当前请求ip
        loginUser.setIpAddr(IpUtils.getIpAddr(request));
        multipleAuth(loginUser);
        chain.doFilter(request, response);
    }

    /**
     * jwt异常直接返回
     * @param response 响应
     * @param msg 错误信息
     */
    private void returnJwtException(HttpServletResponse response,String msg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_OK);
        ServletOutputStream outputStream = response.getOutputStream();
        R<String> result = R.fail(401, msg);
        outputStream.write(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();
    }

    /**
     * @descriptions 多种获取用户信息
     * @author DB
     * @date 2023/09/11 13:49
     * @param userType 用户类型
     * @param username 用户名
     * @return com.pupu.core.base.entity.LoginUser
     */
    private LoginUser multipleLoadUser(UserType userType, String username) {
        LoginUser loginUser = null;
        switch (userType) {
            //系统登陆
            case OAM_USER:
            case MALL_USER:
                loginUser = coreService.loadUserByUsername(username, userType);
                break;
            case MINI_USER:
                // 获取用户的权限等信息
                loginUser = coreService.loadUserByPhone(username, userType, null);
                break;
            default:
        }
        return loginUser;
    }

    /**
     * @Description: 多种认证方式
     * @param loginUser 登陆用户信息
     * @author DB
     * @Date: 2023/8/24 0024 17:12
     */
    private void multipleAuth(LoginUser loginUser) {
        switch (loginUser.getUserType()) {
            case OAM_USER:
            case MALL_USER:
                //构建通用系统用户登陆
                // 构建UsernamePasswordAuthenticationToken,这里密码为null，是因为提供了正确的JWT,实现自动登录
                SysAuthenticationToken sysAuthenticationToken = new SysAuthenticationToken(loginUser.getIdentification(), loginUser, null);
                SecurityContextHolder.getContext().setAuthentication(sysAuthenticationToken);
            break;
            case MINI_USER:
                // MiniProgramAuthenticationToken,实现自动登录
                MiniProgramAuthenticationToken miniProgramAuthenticationToken = new MiniProgramAuthenticationToken(loginUser.getIdentification(), loginUser, null);
                SecurityContextHolder.getContext().setAuthentication(miniProgramAuthenticationToken);
            break;
            default:
        }

    }
}
