package com.datamate.gateway.filter;

import com.alibaba.fastjson2.JSONObject;
import com.datamate.gateway.security.GatewayJwtUtils;
import com.datamate.gateway.security.PermissionRuleMatcher;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

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

/**
 * 用户信息过滤器
 */
@Slf4j
@Component
public class UserContextFilter implements GlobalFilter, Ordered {
    private final GatewayJwtUtils gatewayJwtUtils;
    private final PermissionRuleMatcher permissionRuleMatcher;

    @Value("${datamate.auth.enabled:true}")
    private boolean authEnabled;

    public UserContextFilter(GatewayJwtUtils gatewayJwtUtils, PermissionRuleMatcher permissionRuleMatcher) {
        this.gatewayJwtUtils = gatewayJwtUtils;
        this.permissionRuleMatcher = permissionRuleMatcher;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!authEnabled) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        HttpMethod method = request.getMethod();

        if (!path.startsWith("/api/")) {
            return chain.filter(exchange);
        }
        if (HttpMethod.OPTIONS.equals(method)) {
            return chain.filter(exchange);
        }
        if (permissionRuleMatcher.isWhitelisted(path)) {
            return chain.filter(exchange);
        }

        String token = extractBearerToken(request.getHeaders().getFirst("Authorization"));
        if (!StringUtils.hasText(token)) {
            return writeError(exchange, HttpStatus.UNAUTHORIZED, "auth.0003", "未登录或登录状态已失效");
        }

        Claims claims;
        try {
            if (!gatewayJwtUtils.validateToken(token)) {
                return writeError(exchange, HttpStatus.UNAUTHORIZED, "auth.0003", "登录状态已失效");
            }
            claims = gatewayJwtUtils.getClaimsFromToken(token);
        } catch (Exception ex) {
            log.warn("JWT校验失败: {}", ex.getMessage());
            return writeError(exchange, HttpStatus.UNAUTHORIZED, "auth.0003", "登录状态已失效");
        }

        String requiredPermission = permissionRuleMatcher.resolveRequiredPermission(method, path);
        if (StringUtils.hasText(requiredPermission)) {
            List<String> permissionCodes = gatewayJwtUtils.getStringListClaim(claims, "permissions");
            if (!permissionCodes.contains(requiredPermission)) {
                return writeError(exchange, HttpStatus.FORBIDDEN, "auth.0006", "权限不足");
            }
        }

        String userId = String.valueOf(claims.get("userId"));
        String username = claims.getSubject();
        List<String> roles = gatewayJwtUtils.getStringListClaim(claims, "roles");

        ServerHttpRequest mutatedRequest = request.mutate()
                .header("X-User-Id", userId)
                .header("X-User-Name", username)
                .header("X-User-Roles", String.join(",", roles))
                .build();
        return chain.filter(exchange.mutate().request(mutatedRequest).build());
    }

    @Override
    public int getOrder() {
        return -200;
    }

    private String extractBearerToken(String authorizationHeader) {
        if (!StringUtils.hasText(authorizationHeader)) {
            return null;
        }
        if (!authorizationHeader.startsWith("Bearer ")) {
            return null;
        }
        String token = authorizationHeader.substring("Bearer ".length()).trim();
        return token.isEmpty() ? null : token;
    }

    private Mono<Void> writeError(ServerWebExchange exchange,
                                  HttpStatus status,
                                  String code,
                                  String message) {
        exchange.getResponse().setStatusCode(status);
        exchange.getResponse().getHeaders().set("Content-Type", "application/json;charset=UTF-8");
        byte[] body = JSONObject.toJSONString(new ErrorBody(code, message, null))
                .getBytes(StandardCharsets.UTF_8);
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(body)));
    }

    private record ErrorBody(String code, String message, Object data) {
    }
}
