package com.gameplatform.server.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;

@Component
public class JwtAuthenticationFilter implements WebFilter {
    private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    private final JwtService jwtService;

    public JwtAuthenticationFilter(JwtService jwtService) {
        this.jwtService = jwtService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        final var request = exchange.getRequest();
        final String path = request.getPath().value();
        final String method = request.getMethodValue();

        log.debug("JWT过滤器处理请求: {} {}", method, path);

        final String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

        // 没有 Authorization：不做认证，交给后续链（匿名访问或由安全链控制）
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            return chain.filter(exchange);
        }

        final String token = authHeader.substring(7);

        try {
            Claims claims = jwtService.parse(token);

            Long userId   = claims.get("userId", Long.class);
            String userType = claims.get("userType", String.class);
            String username = claims.get("username", String.class);

            if (userId == null || userType == null || username == null) {
                log.warn("JWT token缺少必要claims");
                return unauthorized(exchange, "Invalid token payload");
            }

            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(
                            username,
                            null,
                            Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + userType.toUpperCase()))
                    );
            authentication.setDetails(claims);

            SecurityContext securityContext = new SecurityContextImpl(authentication);

            log.debug("JWT认证成功 - 用户: {} ({})", username, userType);

            // 把认证信息写入反应式安全上下文
            return chain.filter(exchange)
                    .contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));

        } catch (ExpiredJwtException e) {
            log.debug("JWT token已过期: {}", e.getMessage());
            return unauthorized(exchange, "Token expired");
        } catch (JwtException e) {
            log.warn("JWT token无效: {}", e.getMessage());
            return unauthorized(exchange, "Invalid token");
        } catch (Exception e) {
            log.error("JWT认证异常: {}", e.getMessage(), e);
            return unauthorized(exchange, "Authentication error");
        }
    }

    /**
     * 返回自定义的 401 JSON，并补 CORS 头；写入响应后不再继续过滤链。
     */
    private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
        var response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 给 401 也补 CORS 头，确保前端能拿到状态码/体，避免浏览器当成 Basic 401 处理
        String origin = exchange.getRequest().getHeaders().getFirst(HttpHeaders.ORIGIN);
        if (origin != null) {
            response.getHeaders().set("Access-Control-Allow-Origin", origin);
            response.getHeaders().set("Access-Control-Allow-Credentials", "true");
            response.getHeaders().add("Vary", "Origin");
        }

        // 不返回 WWW-Authenticate，避免浏览器 Basic 弹窗
        response.getHeaders().remove(HttpHeaders.WWW_AUTHENTICATE);

        String body = "{\"code\":401,\"msg\":\"" + escapeJson(message) + "\"}";
        var buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }

    private static String escapeJson(String s) {
        return s == null ? "" : s.replace("\\", "\\\\").replace("\"", "\\\"");
    }
}
