package org.hzhub.gateway.filter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
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 javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * JWT 认证全局过滤器
 * 验证前端传来的 Sa-Token JWT，通过后注入用户信息请求头转发到后端服务
 */
@Slf4j
@Component
public class AuthGlobalFilter implements WebFilter, Ordered {

    @Value("${sa-token.jwt-secret-key:abcdefghijklmnopqrstuvwxyz}")
    private String jwtSecretKey;

    /**
     * 白名单路径（无需认证）
     */
    private static final List<String> WHITE_LIST = List.of(
        "/erp/test/",
        "/erp/customer/",
        "/erp/api/",       // ERP API配置管理（开发阶段暂时跳过认证）
        "/erp/dynamic/",   // ERP动态API执行（开发阶段暂时跳过认证）
        "/actuator/",
        "/error",
        "/auth/login",
        "/auth/register",
        "/auth/code",
        "/auth/tenant/list",
        "/resource/sms/code",
        "/resource/email/code",
        "/resource/sse",
        "/resource/websocket"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();

        // 白名单直接放行
        if (isWhiteList(path)) {
            return chain.filter(exchange);
        }

        // 从 Header 获取 Token
        String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (token == null || !token.startsWith("Bearer ")) {
            return unauthorized(exchange, "未登录或登录已过期");
        }

        String jwtToken = token.substring(7);

        try {
            // 解析并验证 JWT（Sa-Token 简单模式使用 HmacSHA256）
            SecretKey key = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8));
            Claims claims = Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(jwtToken)
                .getPayload();

            // Sa-Token JWT 使用 loginId 字段而非标准 sub 字段
            String loginId = claims.get("loginId", String.class);
            if (loginId == null || loginId.isEmpty()) {
                loginId = claims.getSubject();
            }
            if (loginId == null || loginId.isEmpty()) {
                return unauthorized(exchange, "Token 无效");
            }

            log.debug("JWT 验证成功, loginId={}, path={}", loginId, path);

            // 透传用户信息到后端服务
            String clientId = exchange.getRequest().getHeaders().getFirst("ClientID");
            ServerHttpRequest mutated = exchange.getRequest().mutate()
                .header("X-User-Id", loginId)
                .header("X-Client-Id", clientId != null ? clientId : "")
                .header("X-Gateway-Verified", "true")
                .build();

            return chain.filter(exchange.mutate().request(mutated).build());
        } catch (io.jsonwebtoken.ExpiredJwtException e) {
            log.debug("JWT 已过期, path={}", path);
            return unauthorized(exchange, "登录已过期");
        } catch (Exception e) {
            log.debug("JWT 验证失败, path={}, error={}", path, e.getMessage());
            return unauthorized(exchange, "Token 无效");
        }
    }

    private boolean isWhiteList(String path) {
        return WHITE_LIST.stream().anyMatch(path::startsWith);
    }

    private Mono<Void> unauthorized(ServerWebExchange exchange, String msg) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        String body = "{\"code\":401,\"msg\":\"" + msg + "\"}";
        DataBuffer buffer = exchange.getResponse().bufferFactory()
            .wrap(body.getBytes(StandardCharsets.UTF_8));
        return exchange.getResponse().writeWith(Mono.just(buffer));
    }

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