package com.games.gateway.filters;

import com.games.gateway.enums.BizResultCode;
import com.games.gateway.utils.IpAddressUtils;
import com.games.gateway.utils.TraceUtils;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
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.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;

/**
 * 日志过滤器-打印请求参数返回值及统计执行时长
 *
 * @author: zhenqiang.zhan
 * @create: 2019-09-11
 */
@Configuration
@Slf4j
public class LoggingFilter implements GlobalFilter, Ordered {

    /**
     * 应用application.name
     */
    private static final String APPLICATION_NAME = "application_name";

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

    @Value("${spring.profiles.active}")
    private String runMode;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        TraceUtils.setTraceId(request);
        String path = request.getPath().value();
        String ip = IpAddressUtils.getIpAddress(request);
        String query = request.getURI().getRawQuery();
        String token = exchange.getRequest().getHeaders().getFirst("_tk_");
        //先把请求的日志打印出来
        log.info("==> log path:{} query:{} , ip:{} , token:{}", path, query, ip, token);

        ServerHttpResponse originalResponse = exchange.getResponse();
        AccessRecord accessRecord = new AccessRecord();
        accessRecord.setHost(request.getURI().getHost());
        accessRecord.setPath(request.getURI().getPath());
        accessRecord.setQueryString(request.getQueryParams());
        accessRecord.setHeaders(request.getHeaders());

        //2022-11-24 加一个环境判断，等android重新发布后直接将这行代码废弃掉
        String platform = exchange.getRequest().getHeaders().getFirst("platform");
        BizResultCode.APP_VERSION_ERROR.check("test".equals(runMode) && "ANDROID_5.3.1".equals(platform));


        log.debug(accessRecord.toString());
        Long startTime = System.currentTimeMillis();

        StringBuffer resultBody = new StringBuffer();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                accessRecord.setHttpCode(getStatusCode().value());
                if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        //释放掉内存
                        DataBufferUtils.release(dataBuffer);
                        //responseData就是下游系统返回的内容,可以查看修改
                        resultBody.append(new String(content, Charset.forName("UTF-8")));
                        return bufferFactory.wrap(content);
                    }));
                } else {
                    log.error("响应code异常:{}", getStatusCode());
                }
                return super.writeWith(body);
            }
        };

        //从path路径中获取applicationName,然后设置application
        setApplicationNameHeader(exchange);

        // replace response with decorator
        return chain.filter(exchange.mutate().response(decoratedResponse).build()).doFinally(signal -> {
            accessRecord.setBody(resultBody.toString());
            if (startTime != null) {
                Long expendTime = (System.currentTimeMillis() - startTime);
                accessRecord.setExpendTime(expendTime);
            }
            log.info(accessRecord.toString());
        });
    }

    /**
     * 访问记录对象
     */
    @Data
    @ToString
    private class AccessRecord {
        private String host;
        private String path;
        private String body;
        private MultiValueMap<String, String> queryString;
        private long expendTime;
        private int httpCode;
        private HttpHeaders headers;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append(" --> path:[").append(path).append("]");
            sb.append(", --> expendTime:[").append(expendTime).append("ms").append("]");
            sb.append(", --> host:[").append(host).append("]");
            sb.append(", --> httpCode:[").append(httpCode).append("]");
            sb.append(", --> queryString:").append(queryString);
            sb.append(", --> headers:").append(headers);
            sb.append(", --> body:").append(body);
            return sb.toString();
        }
    }

    /**
     * 从path路径中获取applicationName,然后设置application
     *
     * @param exchange
     */
    private void setApplicationNameHeader(ServerWebExchange exchange) {
        ServerHttpRequest req = exchange.getRequest();
        String path = req.getPath().value();
        String[] split = path.split("/");
        String applicationName = split[1];
        ServerHttpRequest.Builder requestBuilder = req.mutate();
        requestBuilder.headers(k -> k.remove(APPLICATION_NAME));
        requestBuilder.header(APPLICATION_NAME, applicationName);
        ServerHttpRequest request = requestBuilder.build();
        exchange.mutate().request(request).build();
    }
}