package cn.axzo.msg.center.api.fallback;

import cn.axzo.basics.common.exception.ServiceException;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author yanglin
 */
@Slf4j
public abstract class LoggingFallbackFactory implements FallbackFactory<Object> {

    private final Class<?> type;

    public LoggingFallbackFactory(Class<?> apiInterfaceType) {
        if (!apiInterfaceType.isInterface()) {
            throw new RuntimeException(String.format("目标类型必须是接口, 实际上是: %s", apiInterfaceType.getName()));
        }
        this.type = apiInterfaceType;
    }

    @Override
    public Object create(Throwable cause) {
        return Proxy.newProxyInstance(
                getClass().getClassLoader(), new Class[]{type}, new Logging(type, cause));
    }

    @RequiredArgsConstructor
    private static class Logging implements InvocationHandler {

        private final Class<?> type;
        private final Throwable cause;

        @Override
        @SuppressWarnings({"SuspiciousInvocationHandlerImplementation", "代码逻辑做了检查"})
        public Object invoke(Object proxy, Method method, Object[] args) {
            if (ReflectionUtils.isObjectMethod(method)) {
                log.error("method invoking error {}#{}", type.getName(), method.getName(), cause);
                return CommonResponse.error(String.format("调用异常: %s#%s", type.getName(), method.getName()));
            }
            if (method.getReturnType() != CommonResponse.class) {
                throw new RuntimeException(String.format(
                        "调用类型不匹配, 期望: CommonResponse, 实际上是:%s", method.getReturnType().getName()));
            }
            if (cause instanceof ServiceException) {
                Object errorData = getErrorData((ServiceException) cause);
                // 大概率是业务约束不满足, 不打印成error
                log.warn("method invoking exception {}#{}, message={}, errorData={}",
                        type.getName(), method.getName(), cause.getMessage(), errorData, cause);
                return CommonResponse.error(cause.getMessage() == null ? "调用异常" : cause.getMessage());
            } else {
                log.error("method invoking error {}#{}", type.getName(), method.getName(), cause);
                return CommonResponse.error(String.format("调用异常: %s#%s", type.getName(), method.getName()));
            }
        }

        private Object getErrorData(ServiceException e) {
            try {
                Field field = ServiceException.class.getDeclaredField("dataObject");
                field.setAccessible(true);
                return field.get(e);
            } catch (Exception ex) {
                log.error("获取ServiceException中的dataObject出错", ex);
                return null;
            }
        }

    }
}