package com.sikey.wa04.common.interceptor;

import cn.hutool.core.util.StrUtil;
import com.sikey.wa04.common.exception.BusinessException;
import com.sikey.wa04.common.utils.AccessTokenUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.lang.reflect.Method;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Objects;

/**
 * @Author: nelson
 * @Date: 2025/7/3
 * @Description: 认证拦截器
 */
@Slf4j
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Resource
    private RedisOperations<String, String> operations;

    private static final String LOGIN_TOKEN = "login:token:%s";

    private static final String LOGIN_USER_ID = "login:user:id:%s";

    @Override
    @SuppressWarnings("NullableProblems")
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        BusinessException unauthorizedException = new BusinessException(HttpStatus.UNAUTHORIZED.value(), "账号异地登录，请重新登录");
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }

        Method method = handlerMethod.getMethod();
        Authenticated authenticated = method.getAnnotation(Authenticated.class);
        if (authenticated == null) {
            // 放行
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }

        // 开始获取 access token 并且校验是否正确
        String accessToken = request.getHeader("Authorization");
        // log.info("[请求拦截器]参数1：{}", accessToken);
        accessToken = StrUtil.subAfter(accessToken, "Bearer ", true);
        // log.info("[请求拦截器]参数2：{}", accessToken);
        if (Objects.isNull(accessToken)) {
            throw unauthorizedException;
        }

        AccessTokenUtil.AccessTokenBuilder builder;
        try {
            builder = AccessTokenUtil.parse(accessToken);
        } catch (Exception e) {
            log.error("Parse access token error", e);
            throw unauthorizedException;
        }

        // 校验token是否过期
        boolean isExpire = isExpired(unauthorizedException, accessToken, builder.getExpire(), builder.getId());
        if (isExpire) {
            throw unauthorizedException;
        }

        // 将用户信息放入当前请求的上下文中
        CurrentRequestUser.set(builder);
        return true;
    }

    /**
     * 校验token是否过期
     *
     * @param unauthorizedException 提示
     * @param accessToken           accessToken
     * @param expire                expire
     * @param id                    id
     * @return
     */
    private boolean isExpired(BusinessException unauthorizedException, String accessToken, Long expire, String id) {
        String userInfo = operations.opsForValue().get(String.format(LOGIN_TOKEN, accessToken));
        if (Objects.isNull(userInfo) || userInfo.isEmpty()) {
            throw unauthorizedException;
        }

        String latestToken = operations.opsForValue().get(String.format(LOGIN_USER_ID, id));
        if (!accessToken.equals(latestToken)) {
            // 强制登出过期会话
            operations.delete(String.format(LOGIN_TOKEN, accessToken));
            operations.delete(String.format(LOGIN_USER_ID, id));
            throw unauthorizedException;
        }

        LocalDateTime dateTime = Instant.ofEpochMilli(expire)
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        LocalDateTime now = LocalDateTime.now();
        return now.isAfter(dateTime);
    }

}
