package com.gitee.sop.support.dubbo;

import com.alibaba.fastjson2.JSON;
import com.gitee.sop.support.constant.SopConstants;
import com.gitee.sop.support.context.DefaultOpenContext;
import com.gitee.sop.support.context.DefaultWebContext;
import com.gitee.sop.support.context.WebContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcContextAttachment;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcServiceContext;

/**
 * @author 六如
 */
@Slf4j
@Activate(group = {CommonConstants.PROVIDER})
public class DubboProviderTraceFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        initOpenContext();
        initWebContext();
        long startTime = System.currentTimeMillis();
        long endTime = 0;
        try {
            Result result = invoker.invoke(invocation);
            endTime = System.currentTimeMillis();
            return result;
        } finally {
            trace(invocation, startTime, endTime);
        }
    }

    private void initOpenContext() {
        // 从 ServerAttachment 中读取的参数是从 Client 中传递过来的
        RpcContextAttachment serverAttachment = RpcContext.getServerAttachment();
        Object objectAttachment = serverAttachment.getObjectAttachment(SopConstants.OPEN_CONTEXT);
        if (objectAttachment instanceof DefaultOpenContext) {
            DefaultOpenContext openContext = (DefaultOpenContext) objectAttachment;
            openContext.initContext();
        }
    }

    private void initWebContext() {
        RpcContextAttachment serverAttachment = RpcContext.getServerAttachment();
        Object objectAttachment = serverAttachment.getObjectAttachment(SopConstants.WEB_CONTEXT);
        if (objectAttachment instanceof DefaultWebContext) {
            DefaultWebContext webContext = (DefaultWebContext) objectAttachment;
            webContext.initContext();
        }
    }

    private void trace(Invocation invocation, long startTime, long endTime) {
        // 如果是服务提供端
        RpcServiceContext serviceContext = RpcContext.getServiceContext();
        // 获取客户端IP
        String fromIP = serviceContext.getRemoteHost();
        // 获取客户端应用名称
        String fromApp = serviceContext.getRemoteApplicationName();
        // 获取调用的接口
        String interfaceName = invocation.getInvoker().getInterface().getName();
        // 获取调用的方法
        String methodName = serviceContext.getMethodName();
        // 调用参数值
        String param = JSON.toJSONString(serviceContext.getArguments());
        // 超过500毫秒告警
        long spend = (endTime == 0 ? System.currentTimeMillis() : endTime) - startTime;
        if (log.isWarnEnabled() && spend > 500) {
            log.warn("[sop_trace][dubbo_server][time_warn] Dubbo 耗时告警({}ms), from={}({}), methodName={}.{}, param={}",
                    spend, fromApp, fromIP, interfaceName, methodName, param);
        }
    }
}
