package org.xyzh.common.auth.config;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.xyzh.common.auth.contants.AuthContants;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.dto.sys.TbSysPermissionDTO;
import org.xyzh.common.redis.service.RedisService;

/**
 * @description Gateway 认证模式配置 - 信任 Gateway 传递的用户信息
 * @author yslg
 * @since 2025-12-02
 * 
 * 当启用 gateway-mode 时，微服务不再独立验证 JWT，而是信任 Gateway 传递的用户信息
 * 配置项：auth.gateway-mode=true
 */
@Configuration
@ConditionalOnProperty(name = "auth.gateway-mode", havingValue = "true")
public class GatewayAuthConfig {
    private static final Logger logger = LoggerFactory.getLogger(GatewayAuthConfig.class);

    /**
     * Gateway 信任过滤器 - 从请求头获取用户信息
     * 替代 JwtAuthenticationFilter
     */
    @Bean
    public GatewayTrustFilter gatewayTrustFilter() {
        logger.info("启用 Gateway 认证模式，微服务将信任 Gateway 传递的用户信息");
        return new GatewayTrustFilter();
    }

    /**
     * Gateway 信任过滤器实现
     */
    public static class GatewayTrustFilter extends OncePerRequestFilter {
        private static final Logger logger = LoggerFactory.getLogger(GatewayTrustFilter.class);
        private static final String LOGIN_TOKEN_PREFIX = "login:token:";
        private static final String BEARER_PREFIX = "Bearer ";

        @Autowired
        private RedisService redisService;

        @Override
        protected void doFilterInternal(@NonNull HttpServletRequest request, 
                                       @NonNull HttpServletResponse response, 
                                       @NonNull FilterChain filterChain) 
                throws ServletException, IOException {
            
            // 从 Gateway 传递的请求头获取用户信息
            String userId = request.getHeader(AuthContants.USER_ID_ATTRIBUTE);
            String username = request.getHeader(AuthContants.USERNAME_ATTRIBUTE);
            
            if (StringUtils.hasText(userId)) {
                // 从 Redis 获取用户权限
                List<GrantedAuthority> authorities = loadUserAuthorities(request);
                
                // 设置到 Spring Security 上下文
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(userId, null, authorities);
                SecurityContextHolder.getContext().setAuthentication(authentication);
                
                // 同时将用户信息设置到 request attributes 中，供业务代码使用
                request.setAttribute(AuthContants.USER_ID_ATTRIBUTE, userId);
                if (StringUtils.hasText(username)) {
                    request.setAttribute(AuthContants.USERNAME_ATTRIBUTE, username);
                }
                
                logger.info("从 Gateway 获取用户信息: userId={}, username={}, 权限数={}", userId, username, authorities.size());
            }
            
            filterChain.doFilter(request, response);
        }

        /**
         * 从 Redis 加载用户权限列表
         */
        private List<GrantedAuthority> loadUserAuthorities(HttpServletRequest request) {
            List<GrantedAuthority> authorities = new ArrayList<>();
            
            try {
                String token = extractToken(request);
                logger.debug("提取到Token: {}", token != null ? "***" : "null");
                
                if (StringUtils.hasText(token)) {
                    String cacheKey = LOGIN_TOKEN_PREFIX + token;
                    LoginDomain login = redisService.get(cacheKey, LoginDomain.class);
                    logger.debug("Redis key: {}, login: {}", cacheKey, login != null ? "loaded" : "null");
                    
                    if (login != null) {
                        if (login.getUserPermissions() != null) {
                            for (TbSysPermissionDTO permission : login.getUserPermissions()) {
                                // status 为 null 或 true 时都视为有效权限
                                if (permission.getCode() != null && !Boolean.FALSE.equals(permission.getStatus())) {
                                    authorities.add(new SimpleGrantedAuthority(permission.getCode()));
                                }
                            }
                        }
                        logger.debug("加载用户权限: {} 个", authorities.size());
                    }
                }
            } catch (Exception e) {
                logger.warn("加载用户权限失败: {}", e.getMessage(), e);
            }
            
            return authorities;
        }

        /**
         * 从请求头或URL参数提取Token
         */
        private String extractToken(HttpServletRequest request) {
            // 1. 优先从请求头获取
            String authHeader = request.getHeader("Authorization");
            if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
                return authHeader.substring(BEARER_PREFIX.length()).trim();
            }
            
            // 2. 从URL参数获取（用于WebSocket连接）
            String tokenParam = request.getParameter("token");
            if (StringUtils.hasText(tokenParam)) {
                logger.debug("从URL参数获取Token");
                return tokenParam.trim();
            }
            
            return null;
        }
    }
}
