package com.devjob.gateway.security;

import tools.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
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.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * Gateway에서 JWT를 검증하고, 검증된 사용자 ID를 X-User-Id 헤더로 바꿔서
 * 내부 서비스에 전달하는 필터.
 *
 * 라우트 설정에서 `filters: [JwtAuth]`로 적용한다 (factory 이름은 클래스명에서
 * "GatewayFilterFactory"를 뗀 "JwtAuth").
 *
 * - Authorization 헤더가 없거나 형식이 잘못된 경우 401
 * - 토큰이 만료/위조된 경우 401
 * - 검증 성공 시: Authorization 헤더는 제거하고, X-User-Id 헤더를 추가해서
 *   다음 서비스로 전달한다. (내부 서비스는 더 이상 JWT를 직접 검증하지 않는다)
 */
@Component
public class JwtAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<JwtAuthGatewayFilterFactory.Config> {

    private final SecretKey secretKey;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public JwtAuthGatewayFilterFactory(@Value("${jwt.secret}") String secret) {
        super(Config.class);
        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return unauthorized(exchange, "AUTH_REQUIRED", "인증 토큰이 필요합니다.");
            }

            String token = authHeader.substring("Bearer ".length());

            try {
                Claims claims = Jwts.parser()
                        .verifyWith(secretKey)
                        .build()
                        .parseSignedClaims(token)
                        .getPayload();

                String userId = claims.getSubject();

                ServerHttpRequest mutatedRequest = request.mutate()
                        .headers(headers -> {
                            headers.remove(HttpHeaders.AUTHORIZATION);
                            headers.set("X-User-Id", userId);
                        })
                        .build();

                return chain.filter(exchange.mutate().request(mutatedRequest).build());
            } catch (JwtException | IllegalArgumentException e) {
                return unauthorized(exchange, "INVALID_TOKEN", "유효하지 않거나 만료된 토큰입니다.");
            }
        };
    }

    private Mono<Void> unauthorized(ServerWebExchange exchange, String code, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        try {
            byte[] body = objectMapper.writeValueAsBytes(Map.of(
                    "success", false,
                    "errorCode", code,
                    "message", message
            ));
            DataBuffer buffer = response.bufferFactory().wrap(body);
            return response.writeWith(Mono.just(buffer));
        } catch (Exception e) {
            return response.setComplete();
        }
    }

    /** 별도 옵션이 필요 없는 마커 Config. */
    public static class Config {
    }
}
