package com.bjtdba.gateWay.filter;

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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Component
public class ApiAuthFilter extends AbstractGatewayFilterFactory<ApiAuthFilter.Config> {

    @Value("${external.api.secret}")
    private String apiSecret;
    private static final long TIMESTAMP_THRESHOLD = 300000;

    public ApiAuthFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!exchange.getRequest().getPath().value().startsWith("/external")) {
                return chain.filter(exchange);
            }

            String apiKey = exchange.getRequest().getHeaders().getFirst("X-API-KEY");
            String timestamp = exchange.getRequest().getHeaders().getFirst("X-TIMESTAMP");
            String signature = exchange.getRequest().getHeaders().getFirst("X-SIGNATURE");

            if (apiKey == null || timestamp == null || signature == null) {
                return createErrorResponse(exchange, 401, "Missing authentication headers", HttpStatus.UNAUTHORIZED);
            }

            try {
                long requestTime = Long.parseLong(timestamp);
                long currentTime = System.currentTimeMillis();

                if (Math.abs(currentTime - requestTime) > TIMESTAMP_THRESHOLD) {
                    return createErrorResponse(exchange, 401, "Timestamp expired", HttpStatus.UNAUTHORIZED);
                }
            } catch (NumberFormatException e) {
                return createErrorResponse(exchange, 400, "Invalid timestamp format", HttpStatus.BAD_REQUEST);
            }

            String expectedSignature = generateSignature(apiKey, timestamp);
            if (!expectedSignature.equalsIgnoreCase(signature)) {
                return createErrorResponse(exchange, 401, "Invalid signature", HttpStatus.UNAUTHORIZED);
            }

            return chain.filter(exchange);
        };
    }

    /**
     * 创建标准错误响应
     *
     * @param exchange 请求上下文
     * @param code 业务错误码
     * @param message 错误消息
     * @param httpStatus HTTP状态码
     * @return Mono<Void>
     */
    private Mono<Void> createErrorResponse(ServerWebExchange exchange, int code,
                                           String message, HttpStatus httpStatus) {
        exchange.getResponse().setStatusCode(httpStatus);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 构建标准响应格式
        Map<String, Object> responseMap = new HashMap<>();
        responseMap.put("code", code);
        responseMap.put("message", message);
        responseMap.put("data", null);

        // 手动序列化JSON
        String jsonResponse = "{\"code\":" + code +
                ",\"message\":\"" + escapeJson(message) +
                "\",\"data\":null}";

        byte[] bytes = jsonResponse.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);

        return exchange.getResponse().writeWith(Mono.just(buffer));
    }

    /**
     * 转义JSON字符串中的特殊字符
     */
    private String escapeJson(String input) {
        return input.replace("\"", "\\\"")
                .replace("\\", "\\\\")
                .replace("\b", "\\b")
                .replace("\f", "\\f")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
    }

    private String generateSignature(String apiKey, String timestamp) {
        String data = apiKey + "|" + timestamp + "|" + apiSecret;
        return DigestUtils.md5DigestAsHex(data.getBytes());
    }

    public static class Config {}
}