package com.ssyf.common.security.config;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.router.SaRouterStaff;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.ssyf.common.security.handler.SecurityErrorHandler;
import com.ssyf.common.web.holder.Holder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Optional;

/**
 * Sa-Token 整合 jwt (Mixin 混入模式)
 *
 * @author huwei
 * @date 2023/11/4 9:44
 */
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    @Autowired(required = false)
    private Optional<SecurityErrorHandler> securityErrorHandler;

    @Bean
    public StpLogic getStpLogicJwt() {
        return new StpLogicJwtForSimple();
    }


    /**
     * 开启注解
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SaInterceptor().isAnnotation(true)).addPathPatterns("/**");
    }

    /**
     * 注册 [Sa-Token全局过滤器]
     */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                // 指定 拦截路由 与 放行路由
                .addInclude("/**").addExclude("/favicon.ico")
                // 认证函数: 每次请求执行
                .setAuth(this::auth)
                //错误处理
                .setError(this::handleError)
                //认证前置
                .setBeforeAuth(this::beforeAuth);
    }

    private void auth(Object obj){
        SaRouterStaff saRouterStaff = SaRouter.newMatch();

        //路由控制
        Holder.getBeans(RequestConfig.class).forEach(requestAuthConfig -> {
            //认证路由
            if(CollectionUtil.isNotEmpty(requestAuthConfig.math())){
                saRouterStaff.match(requestAuthConfig.math());
            }
            //放行路由
            if(CollectionUtil.isNotEmpty(requestAuthConfig.notMath())){
                saRouterStaff.notMatch(requestAuthConfig.notMath());
            }
        });

        //路由校验
        saRouterStaff.check(()->{
            String sameToken = SaHolder.getRequest().getHeader(SaSameUtil.SAME_TOKEN);
            //同源
            if(SaSameUtil.isValid(sameToken)){
                return;
            }
            //不同源
            StpUtil.checkLogin();

            // 获取剩余过期时间(秒)
            long tokenTimeout = StpUtil.getTokenTimeout();
            // 如果剩余时间小于1小时(3600秒)，则续期
            if(tokenTimeout < 3600) {
                // 续期至默认过期时间(通常为配置的tokenTimeout)
                StpUtil.renewTimeout(SaManager.getConfig().getTimeout());
            }
        });
    }


    private Object handleError(Throwable e){
        if(securityErrorHandler.get() == null){
            throw new RuntimeException(e);
        }
        if(e instanceof NotLoginException){
            return securityErrorHandler.get().notLoginHandle((NotLoginException) e);
        }else if(e instanceof NotPermissionException){
            return securityErrorHandler.get().notPermissionHandle((NotPermissionException) e);
        }else if(e instanceof NotRoleException){
            return securityErrorHandler.get().notRoleHandle((NotRoleException) e);
        }else{
            return securityErrorHandler.get().otherHandle(e);
        }
    }

    private void beforeAuth(Object obj){
        SaResponse saResponse = SaHolder.getResponse();
        //设置请求头
        Holder.getBeans(RequestConfig.class).forEach(requestAuthConfig -> {
            if(CollectionUtil.isNotEmpty(requestAuthConfig.responseHeader())){
                requestAuthConfig.responseHeader().entrySet().forEach(entry->{
                    saResponse.setHeader(entry.getKey() ,entry.getValue());
                });
            }
        });
    }
}
