package cn.novalon.manage.gateway.filter;

import cn.novalon.manage.gateway.service.SignatureService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HttpHeaders;
import org.springframework.http.HttpStatus;
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 java.util.Arrays;
import java.util.List;

/**
 * 请求签名验证过滤器
 * 
 * 文件定义：实现API请求签名验证的全局过滤器
 * 涉及业务：API安全防护，防止请求篡改和重放攻击
 * 算法：HMAC-SHA256签名验证
 * 
 * 验证流程：
 * 1. 检查请求是否在白名单路径中
 * 2. 提取签名相关头部（X-Signature, X-Timestamp, X-Nonce）
 * 3. 验证时间戳是否在有效期内
 * 4. 验证nonce是否已使用（防重放攻击）
 * 5. 重新计算签名并比对
 * 6. 记录nonce防止重放
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@Component
public class SignatureFilter implements GlobalFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(SignatureFilter.class);

    private final SignatureService signatureService;

    @Value("${signature.enabled:true}")
    private boolean signatureEnabled;

    @Value("${signature.secret:${SIGNATURE_SECRET:NovalonManageSystemSecretKey2026}}")
    private String signatureSecret;

    @Value("${signature.whitelist.paths:/actuator/health,/actuator/info}")
    private String whitelistPaths;

    public SignatureFilter(SignatureService signatureService) {
        this.signatureService = signatureService;
        logger.info("SignatureFilter initialized with enabled: {}", signatureEnabled);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!signatureEnabled) {
            logger.debug("Signature verification is disabled");
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();

        if (isWhitelisted(path)) {
            logger.debug("Path {} is whitelisted, skipping signature verification", path);
            return chain.filter(exchange);
        }

        logger.debug("Verifying signature for request: {} {}", request.getMethod(), path);

        boolean isValid = signatureService.verifySignature(request, signatureSecret);

        if (isValid) {
            logger.debug("Signature verification passed for request: {}", path);
            return chain.filter(exchange);
        } else {
            logger.warn("Signature verification failed for request: {} {}", request.getMethod(), path);
            return handleSignatureFailure(exchange);
        }
    }

    private boolean isWhitelisted(String path) {
        if (whitelistPaths == null || whitelistPaths.isEmpty()) {
            return false;
        }

        List<String> whitelistedPaths = Arrays.asList(whitelistPaths.split(","));
        return whitelistedPaths.stream()
                .anyMatch(whitelisted -> path.startsWith(whitelisted.trim()));
    }

    private Mono<Void> handleSignatureFailure(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);

        HttpHeaders headers = response.getHeaders();
        headers.add("X-Error-Code", "INVALID_SIGNATURE");
        headers.add("X-Error-Message", "Request signature verification failed");

        String errorMessage = "{\"error\":\"Unauthorized\",\"code\":\"INVALID_SIGNATURE\"," +
                "\"message\":\"Request signature verification failed. " +
                "Please ensure you have included valid X-Signature, X-Timestamp, and X-Nonce headers.\"}";

        return response.writeWith(Mono.just(response.bufferFactory().wrap(errorMessage.getBytes())));
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 150;
    }
}
