package cn.reghao.tnb.gateway.log;

import cn.reghao.jutil.jdk.web.log.GatewayLog;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.InetSocketAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2024-11-20 16:41:21
 */
@Service
public class GatewayLogService {
    private final RabbitProducer rabbitProducer;

    public GatewayLogService(RabbitProducer rabbitProducer) {
        this.rabbitProducer = rabbitProducer;
    }

    public void saveGatewayLog(GatewayLog gatewayLog) {
        rabbitProducer.sendGatewayLog(gatewayLog);
    }

    public void setGatewayLog(ServerWebExchange exchange) {
        GatewayLog gatewayLog = new GatewayLog();
        setRequestLog(exchange, gatewayLog);
        ServerHttpResponse response = exchange.getResponse();
        int statusCode = Objects.requireNonNull(response.getStatusCode()).value();
        gatewayLog.setStatusCode(statusCode);

        Map<String, String> responseHeaders = new HashMap<>();
        HttpHeaders headers = response.getHeaders();
        headers.forEach((headerName, headerValue) -> {
            responseHeaders.put(headerName, StringUtils.join(headerValue.toArray()));
        });
        gatewayLog.setResponseHeaders(responseHeaders);

        long responseTime = System.currentTimeMillis();
        gatewayLog.setResponseTime(responseTime);
        // 计算执行时间
        long executeTime = responseTime-gatewayLog.getRequestTime();
        gatewayLog.setExecuteTime(executeTime);
        saveGatewayLog(gatewayLog);
    }

    private void setRequestLog(ServerWebExchange exchange, GatewayLog gatewayLog) {
        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        String targetService = (String) exchange.getAttributes().get("targetService");
        if (route != null) {
            gatewayLog.setTargetRoute(route.getId());
            gatewayLog.setTargetService(targetService);
        }

        ServerHttpRequest request = exchange.getRequest();
        String requestUrl = getOriginalRequestUrl(exchange);
        gatewayLog.setRequestUrl(requestUrl);
        gatewayLog.setRequestMethod(request.getMethod().name());
        Map<String, String> requestHeaders = new HashMap<>();
        HttpHeaders headers = request.getHeaders();
        headers.forEach((headerName, headerValue) -> {
            requestHeaders.put(headerName, StringUtils.join(headerValue.toArray()));
        });
        gatewayLog.setRequestHeaders(requestHeaders);
        String requestId = headers.get("x-request-id").get(0);
        gatewayLog.setRequestId(requestId);
        String requestTimeStr = headers.get("x-request-time").get(0);
        long requestTime = Long.parseLong(requestTimeStr);
        gatewayLog.setRequestTime(requestTime);
        String realRemoteAddr = "";
        if (headers.get("X-Real-Remote") != null && !headers.get("X-Real-Remote").isEmpty()) {
            realRemoteAddr = headers.get("X-Real-Remote").get(0);
        }

        InetSocketAddress remote = exchange.getRequest().getRemoteAddress();
        if (remote != null) {
            if (!realRemoteAddr.isBlank()) {
                gatewayLog.setRemoteAddr(realRemoteAddr);
            } else {
                gatewayLog.setRemoteAddr(remote.getHostString());
            }
            //gatewayLog.setRemoteAddr(remote.getHostString());
            gatewayLog.setRemotePort(remote.getPort());
        }

        Map<String, String> cookieMap = request.getCookies().values().stream()
                .map(list -> list.get(0))
                .collect(Collectors.toMap(HttpCookie::getName, HttpCookie::getValue));
        String fingerprint = cookieMap.get("fp");
        if (fingerprint != null) {
            gatewayLog.setFingerprint(fingerprint);
        }
    }

    private String getOriginalRequestUrl(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        // GATEWAY_REQUEST_URL_ATTR 由 RouteToRequestUrlFilter 设置, 它的执行顺序在本 filter 后面, 所以不能使用它
        //LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        //URI requestUri = uris.stream().findFirst().orElse(request.getURI());
        URI requestUri = request.getURI();
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();
    }
}
