package com.microservices.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.constant.ExceptionMsgConstants;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Slf4j
@Component
public class GlobalResponseFilter implements GlobalFilter, Ordered {
    @Autowired
    private RedisService redisService;

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

        String lowerUrl = request.getURI().getPath().toLowerCase();
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        // 当请求为Nacos时，Nacos返回的所有非JSON响应都会被自动转换为结构化的错误信息，同时将HTTP状态码设置为200，适合用于规范化微服务架构的响应格式。
        if (lowerUrl.startsWith("/nacos")) {

            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    HttpStatus status = getStatusCode() != null ? getStatusCode() : HttpStatus.INTERNAL_SERVER_ERROR;
                    if (!status.isError()) {
                        return super.writeWith(body);
                    }
                    if (status == HttpStatus.FORBIDDEN) {
                        log.error("nacos鉴权异常，已清理redis中nacos token");
                        // 设置新的状态码为 200
                        setStatusCode(HttpStatus.OK);
                        // 设置响应内容类型为 JSON
                        getHeaders().setContentType(MediaType.APPLICATION_JSON);
                        // 清理Sentinel失效token
                        redisService.deleteObject(CacheConstants.NACOS_TOKEN);
                        // 创建自定义的 JSON 响应内容
                        String jsonBody = JSON.toJSONString(AjaxResult.error(ExceptionMsgConstants.RETRY_ERROR));
                        byte[] bytes = jsonBody.getBytes(StandardCharsets.UTF_8);
                        getHeaders().setContentLength(bytes.length);
                        DataBuffer dataBuffer = bufferFactory.wrap(bytes);
                        // 返回新的响应内容
                        return super.writeWith(Mono.just(dataBuffer));
                    }

                    return Flux.from(body).<String>handle((dataBuffer, synchronousSink) -> {
                        if (status.isError()) {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            //释放掉内存
                            DataBufferUtils.release(dataBuffer);
                            String result = new String(bytes, StandardCharsets.UTF_8);
                            synchronousSink.next(result);
                            return;
                        }
                        synchronousSink.complete();

                    }).flatMap(resStr -> {
                        // 保留原始状态码
                        originalResponse.setStatusCode(HttpStatus.OK);
                        String errMsg;
                        if (!JSON.isValid(resStr)) {
                            errMsg = resStr;
                        } else {
                            JSONObject resJson = JSONObject.parseObject(resStr);
                            if (resJson.containsKey("message")) {
                                errMsg = resJson.getString("message");
                            } else {
                                errMsg = ExceptionMsgConstants.SYSTEM_EXEC_ERROR;
                            }
                        }
                        byte[] bytes = JSON.toJSONBytes(AjaxResult.error(errMsg));
                        getHeaders().setContentLength(bytes.length);
                        DataBuffer buffer = bufferFactory.wrap(bytes);
                        return super.writeWith(Mono.just(buffer));
                    }).then();
                }
            };

            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        }
        if (lowerUrl.startsWith("/portainer")) {
            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    HttpStatus status = getStatusCode() != null ? getStatusCode() : HttpStatus.INTERNAL_SERVER_ERROR;
                    if (!status.isError()) {
                        return super.writeWith(body);
                    }

                    return Flux.from(body).<String>handle((dataBuffer, synchronousSink) -> {
                        if (status.isError()) {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            //释放掉内存
                            DataBufferUtils.release(dataBuffer);
                            String result = new String(bytes, StandardCharsets.UTF_8);
                            synchronousSink.next(result);
                        }
                        synchronousSink.complete();
                    }).flatMap(resStr -> {
                        log.error("Portainer响应处理异常，异常信息：{}", resStr);
                        originalResponse.setStatusCode(HttpStatus.OK);
                        byte[] bytes;
                        if (JSON.isValid(resStr)) {
                            JSONObject resJson = JSONObject.parseObject(resStr);
                            if (resJson.containsKey("message")) {
                                String message = resJson.getString("message");
                                if (StringUtils.isNotBlank(message) && message.contains("A valid authorisation token is missing")) {
                                    log.error("Portainer鉴权异常，已清理redis中Portainer token");
                                    //token失效清除redis缓存
                                    redisService.deleteObject(CacheConstants.PORTAINER_TOKEN);
                                    bytes = JSON.toJSONBytes(AjaxResult.error(ExceptionMsgConstants.RETRY_ERROR));
                                } else {
                                    bytes = JSON.toJSONBytes(AjaxResult.error(message));
                                }
                            } else {
                                bytes = JSON.toJSONBytes(AjaxResult.error(ExceptionMsgConstants.SYSTEM_EXEC_ERROR));
                            }
                        } else {
                            bytes = JSON.toJSONBytes(AjaxResult.error(ExceptionMsgConstants.SYSTEM_EXEC_ERROR));
                        }
                        getHeaders().setContentLength(bytes.length);
                        DataBuffer buffer = bufferFactory.wrap(bytes);
                        return super.writeWith(Mono.just(buffer));
                    }).then();
                }
            };

            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        }
        if (lowerUrl.startsWith("/sentinel")) {

            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    HttpStatus status = getStatusCode() != null ? getStatusCode() : HttpStatus.INTERNAL_SERVER_ERROR;
                    if (!status.isError()) {
                        return super.writeWith(body);
                    }
                    if (status == HttpStatus.UNAUTHORIZED) {
                        log.error("sentinel鉴权异常，已清理redis中sentinel token");
                        // 设置新的状态码为 200
                        setStatusCode(HttpStatus.OK);
                        // 设置响应内容类型为 JSON
                        getHeaders().setContentType(MediaType.APPLICATION_JSON);
                        // 清理Sentinel失效token
                        redisService.deleteObject(CacheConstants.SENTINEL_TOKEN);
                        // 创建自定义的 JSON 响应内容
                        String jsonBody = JSON.toJSONString(AjaxResult.error(ExceptionMsgConstants.RETRY_ERROR));
                        byte[] bytes = jsonBody.getBytes(StandardCharsets.UTF_8);
                        getHeaders().setContentLength(bytes.length);
                        DataBuffer dataBuffer = bufferFactory.wrap(bytes);
                        // 返回新的响应内容
                        return super.writeWith(Mono.just(dataBuffer));
                    }

                    return Flux.from(body).<String>handle((dataBuffer, synchronousSink) -> {
                        if (status.isError()) {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            //释放掉内存
                            DataBufferUtils.release(dataBuffer);
                            String result = new String(bytes, StandardCharsets.UTF_8);
                            synchronousSink.next(result);
                        }
                        synchronousSink.complete();
                    }).flatMap(resStr -> {
                        log.error("Sentinel响应处理异常，异常信息：{}", resStr);
                        byte[] bytes;
                        bytes = JSON.toJSONBytes(AjaxResult.error(ExceptionMsgConstants.SYSTEM_EXEC_ERROR));
                        setStatusCode(HttpStatus.OK);

                        getHeaders().setContentLength(bytes.length);
                        DataBuffer buffer = bufferFactory.wrap(bytes);
                        return super.writeWith(Mono.just(buffer));
                    }).then();
                }
            };

            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        }
        return chain.filter(exchange);

    }

    @Override
    public int getOrder() {
        //WRITE_RESPONSE_FILTER 之前执行
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }
}