package com.qiuguo.iot.box.websocket.api.filter;

import lombok.extern.slf4j.Slf4j;
import org.hswebframework.web.logger.ReactiveLogger;
import org.reactivestreams.Publisher;
import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
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 org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * <p>
 * </p>*日志配置
 * @author wulin
 * @since 2023-08-07
 */
@Configuration
@Slf4j
public class LogWebFilter implements WebFilter {
    String customerIp = "";
    public static String HEAD_IP = "customerIP";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        long startTime = System.currentTimeMillis();
        ServerHttpRequest request = exchange.getRequest();
        String requestId = request.getId();
        MDC.put(LogMdcConfiguration.PRINT_LOG_ID, requestId);

        customerIp = request.getRemoteAddress().getAddress().getHostAddress();//.getHostName();
        String m = request.getMethod().toString();

        log.info("api start time:{} ip:{} method:{} url:{} param:{} headers:{}",
                startTime,
                customerIp,
                m,
                request.getPath(),
                request.getQueryParams(),
                request.getHeaders());

        ServerWebExchange.Builder ex = exchange.mutate();

        ex.response(getResponse(exchange, requestId));

       // if(!request.getMethod().equals(HttpMethod.GET) && !request.getMethod().equals(HttpMethod.DELETE)){
        ex.request(getRequest(exchange));

        return chain.filter(ex.build())
                .contextWrite(context -> {
                    Context contextTmp = context.put(LogMdcConfiguration.PRINT_LOG_ID, requestId);

                    return contextTmp;
                })
                .doFinally(signalType -> {
                    long endTime = System.currentTimeMillis();
                    MDC.put(LogMdcConfiguration.PRINT_LOG_ID, requestId);
                    log.info("api end time:{}, total time:{}", endTime, endTime - startTime);
                    MDC.remove(LogMdcConfiguration.PRINT_LOG_ID);
                });
    }

    private ServerHttpRequest getRequest(ServerWebExchange exchange){
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest newRequest = new ServerHttpRequestDecorator(request){
            @Override
            public Flux<DataBuffer> getBody() {
                Flux<DataBuffer> body = this.getDelegate().getBody();
                return body.map(dataBuffer -> {
                    log.info("request:{}", dataBuffer.toString(StandardCharsets.UTF_8));
                    return dataBuffer;
                });
            }
            @Override
            public HttpHeaders getHeaders(){
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                httpHeaders.set(HEAD_IP, customerIp);
                return httpHeaders;
            }
        };
        return newRequest;
    }

    private ServerHttpResponse getResponse(ServerWebExchange exchange, String requestId){
        ServerHttpResponse response = exchange.getResponse();
        return new ServerHttpResponseDecorator(response){
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body){
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer joinBuffer = dataBufferFactory.join(dataBuffers);
                        byte[] returnContent = new byte[joinBuffer.readableByteCount()];
                        joinBuffer.read(returnContent);
                        DataBufferUtils.release(joinBuffer);
                        String returnStr = new String(returnContent, StandardCharsets.UTF_8);
                        log.info("response:{}", returnStr);
                        return response.bufferFactory().wrap(returnContent);
                    }));
                }else if(body instanceof  Mono){
                    Mono<DataBuffer> monoBody = Mono.from(body);
                    return super.writeWith(monoBody.map(dataBuffer -> {
                        log.info("response:{}", dataBuffer.toString(StandardCharsets.UTF_8));
                        return response.bufferFactory().wrap(dataBuffer.toString(StandardCharsets.UTF_8).getBytes());
                    }));
                }
                return super.writeWith(body);
            }
        };
    }

}
