package com.lesingle.security.filter;

import com.lesingle.common.constants.CacheConstants;
import com.lesingle.common.constants.RoleConstants;
import com.lesingle.modules.sys.mapper.SysUserMapper;
import com.lesingle.security.model.LoginUser;
import com.lesingle.security.util.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * JWT 认证过滤器
 * 从 Authorization 头中解析 Bearer token，验证后设置 SecurityContext
 * 对于公开接口，解析失败不阻断请求（设置 null 用户）
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final StringRedisTemplate redisTemplate;
    private final SysUserMapper sysUserMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String token = extractToken(request);

        if (StringUtils.hasText(token)) {
            try {
                // 检查 Token 是否在黑名单中（已登出或已失效）
                String blacklistKey = CacheConstants.TOKEN_BLACKLIST_PREFIX + token;
                if (Boolean.TRUE.equals(redisTemplate.hasKey(blacklistKey))) {
                    log.debug("Token 已被注销（黑名单），拒绝访问");
                    filterChain.doFilter(request, response);
                    return;
                }

                Claims claims = jwtUtil.parseToken(token);
                Long userId = Long.parseLong(claims.getSubject());
                String username = claims.get("username", String.class);
                Long tenantId = ((Number) claims.get("tenantId")).longValue();

                // 从 Redis 获取用户角色和权限（如果 Redis 不可用则从 token 中获取）
                LoginUser loginUser = buildLoginUser(userId, username, tenantId);

                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (Exception e) {
                log.debug("Token 解析失败：{}", e.getMessage());
                // 不阻断请求，让 Spring Security 根据配置决定是否放行
            }
        }

        filterChain.doFilter(request, response);
    }

    private String extractToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    /**
     * 构建登录用户对象
     * 优先从 Redis 获取角色/权限，Redis 不可用时回退到数据库查询
     */
    private LoginUser buildLoginUser(Long userId, String username, Long tenantId) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(userId);
        loginUser.setUsername(username);
        loginUser.setTenantId(tenantId);

        Set<String> roles = new HashSet<>();
        Set<String> permissions = new HashSet<>();

        // 优先从 Redis 获取
        try {
            String rolesKey = CacheConstants.USER_ROLES_PREFIX + userId;
            String permsKey = CacheConstants.USER_PERMS_PREFIX + userId;
            Set<String> cachedRoles = redisTemplate.opsForSet().members(rolesKey);
            Set<String> cachedPerms = redisTemplate.opsForSet().members(permsKey);

            if (cachedRoles != null && !cachedRoles.isEmpty()) {
                roles = cachedRoles;
                permissions = cachedPerms != null ? cachedPerms : new HashSet<>();
            } else {
                // Redis 无缓存，回退到数据库查询
                roles = new HashSet<>(sysUserMapper.selectRolesByUserId(userId));
                permissions = new HashSet<>(sysUserMapper.selectPermissionsByUserId(userId));
            }
        } catch (Exception e) {
            // Redis 不可用，直接从数据库查询
            log.debug("Redis 不可用，从数据库查询用户权限：{}", e.getMessage());
            try {
                roles = new HashSet<>(sysUserMapper.selectRolesByUserId(userId));
                permissions = new HashSet<>(sysUserMapper.selectPermissionsByUserId(userId));
            } catch (Exception dbEx) {
                log.warn("数据库查询用户权限失败：{}", dbEx.getMessage());
            }
        }

        loginUser.setRoles(roles);
        loginUser.setPermissions(permissions);
        loginUser.setSuperAdmin(roles.contains(RoleConstants.SUPER_ADMIN));

        return loginUser;
    }
}
