package dev.rheinsw.gateway.filter;

import dev.rheinsw.gateway.service.ApiKeyValidator;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author Thatsaphorn Atchariyaphap
 * @since 24.04.25
 */
@Component
@Order(-1)
@RequiredArgsConstructor
public class ApiKeyGatewayFilter implements GlobalFilter {

    private static final Logger log = LoggerFactory.getLogger(ApiKeyGatewayFilter.class);

    @Value("${gateway.security.frontend-origin}")
    private String expectedFrontendOrigin;

    private final ApiKeyValidator apiKeyValidator;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        log.debug("Incoming request for path: {}", path);

        if (path.startsWith("/api/public/")) {
            log.debug("Public path detected, skipping API key validation");
            return chain.filter(exchange);
        }

        String frontendKey = exchange.getRequest().getHeaders().getFirst("X-Frontend-Key");
        String internalKey = exchange.getRequest().getHeaders().getFirst("X-Internal-Auth");
        String targetService = resolveTargetServiceFromExchange(exchange);
        boolean isFrontend = isFrontendRequest(exchange);

        log.debug("Target service resolved: {}, isFrontend: {}", targetService, isFrontend);

        if (frontendKey != null && isFrontend) {
            log.debug("Validating frontend API key for service: {}", targetService);
            if (apiKeyValidator.isAuthorized(frontendKey, targetService, true)) {
                log.debug("Frontend API key authorized");
                return chain.filter(exchange);
            }
            log.warn("Shared API key validation failed for service: {}", targetService);
        }

        if (internalKey != null) {
            log.debug("Validating internal API key for service: {}", targetService);
            if (apiKeyValidator.isAuthorized(internalKey, targetService, false)) {
                log.debug("Internal API key authorized");
                return chain.filter(exchange);
            }
            log.warn("Internal API key validation failed for service: {}", targetService);
        }

        log.warn("Unauthorized request to {} from origin {}", path, exchange.getRequest().getHeaders().getFirst("Origin"));
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

    private String resolveTargetServiceFromExchange(ServerWebExchange exchange) {
        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        return route != null ? route.getId() : "unknown";
    }

    private boolean isFrontendRequest(ServerWebExchange exchange) {
        String origin = exchange.getRequest().getHeaders().getFirst("Origin");
        boolean matches = origin != null && origin.trim().equalsIgnoreCase(expectedFrontendOrigin.trim());
        log.debug("Origin header: {}, expected: {}, matches: {}", origin, expectedFrontendOrigin, matches);
        return matches;
    }

}
