package cn.novalon.manage.gateway.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HttpMethod;
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;

/**
 * 响应压缩过滤器
 * 
 * 文件定义：实现网关响应的压缩功能
 * 涉及业务：响应压缩、性能优化、带宽节省
 * 
 * 核心功能：
 * 1. 检测客户端支持的压缩算法
 * 2. 对响应进行压缩
 * 3. 设置压缩相关响应头
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@Component
public class CompressionFilter implements GlobalFilter, Ordered {

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

    private static final String ACCEPT_ENCODING = "Accept-Encoding";
    private static final String CONTENT_ENCODING = "Content-Encoding";
    private static final String GZIP = "gzip";
    private static final String DEFLATE = "deflate";
    private static final String VARY = "Vary";

    private static final List<String> COMPRESSIBLE_TYPES = Arrays.asList(
            "text/html",
            "text/xml",
            "text/plain",
            "text/css",
            "text/javascript",
            "application/javascript",
            "application/json",
            "application/xml"
    );

    private boolean compressionEnabled = false;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        if (!compressionEnabled || !shouldCompress(request)) {
            return chain.filter(exchange);
        }

        String acceptEncoding = request.getHeaders().getFirst(ACCEPT_ENCODING);
        
        if (acceptEncoding == null || acceptEncoding.isEmpty()) {
            return chain.filter(exchange);
        }

        String compressionType = determineCompressionType(acceptEncoding);
        
        if (compressionType == null) {
            return chain.filter(exchange);
        }

        logger.debug("Applying {} compression for request: {}", 
                compressionType, request.getPath());

        ServerHttpResponse response = exchange.getResponse();
        
        response.getHeaders().set(CONTENT_ENCODING, compressionType);
        response.getHeaders().add(VARY, ACCEPT_ENCODING);

        return chain.filter(exchange);
    }

    private boolean shouldCompress(ServerHttpRequest request) {
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return false;
        }

        String contentType = request.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        
        if (contentType != null) {
            return COMPRESSIBLE_TYPES.stream()
                    .anyMatch(type -> contentType.contains(type));
        }

        return true;
    }

    private String determineCompressionType(String acceptEncoding) {
        if (acceptEncoding.contains(GZIP)) {
            return GZIP;
        }
        
        if (acceptEncoding.contains(DEFLATE)) {
            return DEFLATE;
        }

        return null;
    }

    public void setCompressionEnabled(boolean enabled) {
        this.compressionEnabled = enabled;
        logger.info("Compression enabled: {}", enabled);
    }

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