package com.lanyuanxiaoyao.service.forest.configuration;

import cn.hutool.core.util.ObjectUtil;
import com.dtflys.forest.exceptions.ForestRuntimeException;
import com.dtflys.forest.http.ForestQueryMap;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestResponse;
import com.dtflys.forest.interceptor.Interceptor;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Maps;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.map.MutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 指标
 *
 * @author lanyuanxiaoyao
 * @date 2024-01-31
 */
@SuppressWarnings("unchecked")
@Component
public class MetricsInterceptor implements Interceptor<Object> {
    public static final String FOREST_SEND_REQUEST = "service_forest_send_request";
    public static final String FOREST_SEND_REQUEST_SECONDS = "service_forest_send_request_seconds";
    private static final Logger logger = LoggerFactory.getLogger(MetricsInterceptor.class);
    private static final String PARSED_TAGS = "parsed_tags";
    private final MeterRegistry meterRegistry;

    public MetricsInterceptor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    private Iterable<Tag> parseTags(ForestRequest<?> request) {
        MutableList<Tag> tags = Lists.mutable.of(
                Tag.of("host", request.getHost()),
                Tag.of("port", String.valueOf(request.getPort())),
                Tag.of("path", request.getPath()),
                Tag.of("schema", request.getScheme())
        );
        ForestQueryMap query = request.getQuery();
        if (ObjectUtil.isNotEmpty(query)) {
            for (Map.Entry<String, Object> entry : query.entrySet()) {
                tags.add(Tag.of("query_" + entry.getKey(), String.valueOf(entry.getValue())));
            }
        }
        return tags;
    }

    private void increaseCounter(Integer code, MutableList<Tag> tags) {
        meterRegistry.counter(
                MetricsInterceptor.FOREST_SEND_REQUEST,
                tags.with(Tag.of("code", String.valueOf(code)))
        ).increment();
    }

    @Override
    public boolean beforeExecute(ForestRequest request) {
        addAttribute(request, PARSED_TAGS, parseTags(request));
        return Interceptor.super.beforeExecute(request);
    }

    @Override
    public void afterExecute(ForestRequest request, ForestResponse response) {
        Interceptor.super.afterExecute(request, response);

        MutableList<Tag> tags = (MutableList<Tag>) getAttribute(request, PARSED_TAGS);
        meterRegistry.timer(FOREST_SEND_REQUEST_SECONDS, tags)
                .record(response.getTimeAsMillisecond(), TimeUnit.MILLISECONDS);
    }

    @Override
    public void onSuccess(Object data, ForestRequest request, ForestResponse response) {
        Interceptor.super.onSuccess(data, request, response);

        MutableList<Tag> tags = (MutableList<Tag>) getAttribute(request, PARSED_TAGS);
        if (ObjectUtil.isNotNull(tags)) {
            increaseCounter(response.statusCode(), tags);
        }
    }

    @Override
    public void onError(ForestRuntimeException ex, ForestRequest request, ForestResponse response) {
        Interceptor.super.onError(ex, request, response);

        MutableList<Tag> tags = (MutableList<Tag>) getAttribute(request, PARSED_TAGS);
        if (ObjectUtil.isNotNull(tags)) {
            increaseCounter(response.statusCode(), tags);
        }
    }
}
