package com.bonus.gateway.filter;

import com.bonus.common.core.exception.CaptchaException;
import com.bonus.common.core.utils.encryption.Sm3Util;
import com.bonus.common.core.utils.encryption.Sm4Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
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.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 请求内容存储 处理请求内容 内容放在gatewayContext中
 * 解决数据流被重复读取无数据的 问题
 * 对formData 数据进行解密
 *
 * @author bonus
 */

@Component
@Slf4j
public class RequestCoverFilter implements GlobalFilter, Ordered {

/*    *//**
     * 是否启用解密功能
     *//*
    @Value("${system.encryptRequest}")
    private boolean encryptRequest;
    *//**
     * 是否启用数据完整性校验功能
     *//*
    @Value("${system.integrity}")
    private boolean integrity;*/
    /**
     * 数据加密标志
     */
    public static final String ENCRYPT = "encryptRequest";
    /**
     * 数据完整性校验标志
     */
    public static final String INTEGRALITY = "checkIntegrity";
    /**
     * 完整性校验哈希值
     */
    public static final String HMAC_HEADER_NAME = "Params-Hash";

    /**
     * 处理Web请求，并（可选地）通过给定的 {@link GatewayFilterChain} 委托给下一个 {@code GatewayFilter}。
     *
     * @param exchange 当前服务器交换
     * @param chain    提供委托给下一个过滤器的方式
     * @return {@code Mono<Void>} 表示请求处理完成的指示
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 如果解密和完整性校验均未启用，则直接通过过滤链
        ServerHttpRequest request = exchange.getRequest();
        MediaType contentType = request.getHeaders().getContentType();
        if (contentType == null) {
            log.info("请求头中无Content-Type信息，直接继续过滤链。");
            return chain.filter(exchange);
//            return handleUrlParams(exchange, chain);
        } else if (contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
            return handleBodyRequest(exchange, chain);
        } else {
            return chain.filter(exchange);
        }
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }

    /**
     * 处理请求体里的参数
     *
     * @param exchange 当前服务器交换
     * @param chain    提供委托给下一个过滤器的方式
     * @return {@code Mono<Void>} 表示请求处理完成的指示
     */
    private Mono<Void> handleBodyRequest(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        boolean integrality = "true".equalsIgnoreCase(request.getHeaders().getFirst(INTEGRALITY));
        boolean encrypt = "true".equalsIgnoreCase(request.getHeaders().getFirst(ENCRYPT));
        return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
            byte[] body = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(body);
            DataBufferUtils.release(dataBuffer);
            String requestBody = new String(body, StandardCharsets.UTF_8);

            // 去掉多余的引号（如果有）
            if (requestBody.startsWith("\"") && requestBody.endsWith("\"")) {
                requestBody = requestBody.substring(1, requestBody.length() - 1);
            }

            if (ObjectUtils.isEmpty(requestBody)) {
                return exchange.getResponse().setComplete();
            }
            // 解密请求体
            if (encrypt) {
                try {
                    requestBody = Sm4Utils.decrypt(requestBody);
                } catch (Exception e) {
                    log.error("解密请求体时发生错误: {}", e.getMessage(), e);
                    exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                    return exchange.getResponse().setComplete();
                }
            }
            if (ObjectUtils.isEmpty(requestBody)) {
                return exchange.getResponse().setComplete();
            }

            // 校验数据完整性
            if (integrality) {
                String providedHmac = requestBody.split("\\|")[1];
                integrityVerification(providedHmac, requestBody.split("\\|")[0]);
            }
            requestBody = requestBody.split("\\|")[0];
            if (ObjectUtils.isEmpty(requestBody)) {
                return exchange.getResponse().setComplete();
            }

            // 创建新的请求体
            DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
            DataBuffer newBody = bufferFactory.wrap(requestBody.getBytes(StandardCharsets.UTF_8));
            ServerHttpRequest newRequest = createNewRequest(exchange, newBody);

            return chain.filter(exchange.mutate().request(newRequest).build());
        });
    }

    /**
     * 创建包含新请求体的请求
     *
     * @param exchange 当前服务器交换
     * @param newBody  新的请求体数据缓冲区
     * @return 新的ServerHttpRequest对象
     */
    private ServerHttpRequest createNewRequest(ServerWebExchange exchange, DataBuffer newBody) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public Flux<DataBuffer> getBody() {
                return Flux.just(newBody);
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());
                headers.remove(HttpHeaders.CONTENT_LENGTH);
                headers.setContentLength(newBody.readableByteCount());
                return headers;
            }
        };
    }

    /**
     * 处理url后拼接的参数
     *
     * @param exchange 当前服务器交换
     * @param chain    提供委托给下一个过滤器的方式
     * @return {@code Mono<Void>} 表示请求处理完成的指示
     */
    private Mono<Void> handleUrlParams(ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            ServerWebExchange updatedExchange = updateRequestParam(exchange);
            if (updatedExchange != null) {
                return chain.filter(updatedExchange);
            } else {
                return chain.filter(exchange);
            }
        } catch (Exception e) {
            log.error("处理 GET 请求时发生错误: {}", e.getMessage(), e);
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            return exchange.getResponse().setComplete();
        }
    }

    /**
     * 更新查询参数，解密和验证数据完整性
     *
     * @param exchange 当前服务器交换
     * @return 更新后的ServerWebExchange对象，如果无需更新则返回null
     */
    private ServerWebExchange updateRequestParam(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        boolean integrality = "true".equalsIgnoreCase(request.getHeaders().getFirst(INTEGRALITY));
        boolean encrypt = "true".equalsIgnoreCase(request.getHeaders().getFirst(ENCRYPT));
        URI uri = request.getURI();
        String query = uri.getQuery();

        if (!ObjectUtils.isEmpty(query)) {
            // 解密查询参数
            if (encrypt) {
                try {
                    query = Sm4Utils.decrypt(query);
                } catch (Exception e) {
                    log.error("解密查询参数时发生错误: {}", e.getMessage(), e);
                    throw new CaptchaException("请求参数不正确");
                }
            }

            // 校验数据完整性
            // 校验数据完整性
            if (integrality) {
                integrityVerification(query.split("\\|")[1], query.split("\\|")[0]);
            }

            if (ObjectUtils.isEmpty(query)) {
                return null;
            }

            // 更新查询参数
            Map<String, List<String>> queryParams = Arrays.stream(query.split("&"))
                    .map(param -> param.split("="))
                    .collect(Collectors.toMap(param -> param[0], param -> Collections.singletonList(param[1])));
            URI newUri = UriComponentsBuilder.fromUri(uri)
                    .replaceQueryParams(CollectionUtils.toMultiValueMap(queryParams))
                    .build(true)
                    .toUri();

            ServerHttpRequest newRequest = request.mutate().uri(newUri).build();
            return exchange.mutate().request(newRequest).build();
        }
        return null;
    }

    /**
     * 数据完整性校验
     *
     * @param providedHmac 请求头中的 HMAC 值
     * @param query        请求参数
     */
    private void integrityVerification(String providedHmac, String query) {
        if (providedHmac == null) {
            log.error("请求头中缺少 Params-Hash");
            throw new CaptchaException("请求参数不正确");
        }
        String encrypt = Sm3Util.encrypt(query);
        log.debug("加密后的参数: {}", encrypt);
        log.debug("请求头中的 Params-Hash: {}", providedHmac);
        if (!encrypt.equals(providedHmac)) {
            log.error("参数校验失败");
            throw new CaptchaException("请求参数不正确");
        }
    }

}

