package quant.rich.emoney.service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.nano.MessageNano;

import jakarta.annotation.PostConstruct;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.annotation.ResponseDecodeExtension;
import quant.rich.emoney.util.SpringBeanDetector;
import quant.rich.emoney.util.SpringContextHolder;

/**
 * 负责对请求和返回进行拦截和进一步处理
 * @see ResponseDecodeExtension
 * @see quant.rich.emoney.client.EmoneyClient
 * @see quant.rich.emoney.controller.api.ProtoDecodeController
 */
@Service
@Slf4j
public class RequestResponseInspectService {

    @Autowired
    Reflections reflections;
    
    @Autowired
    ObjectMapper objectMapper;

    Map<String, List<MethodInfo>> responseDecodeExtensions = new HashMap<String, List<MethodInfo>>();
    
    @Data
    @RequiredArgsConstructor
    private static class MethodInfo {
        /**
         * 对应的 Inspect 方法
         */
        final Method method;
        /**
         * 声明了 Inspect 方法的类
         */
        final Class<?> declaringClass;
        /**
         * Inspect 顺序
         */
        final Integer order;
        /**
         * 声明了 Inspect 方法的类的实例。当方法是成员方法且实例受 Spring 管理时，通过实例调用
         */
        Object instance;
        /**
         * 是否在模拟客户端中启用。该参数从注解处填充
         */
        boolean isClient = true;
        
    }
    
    @PostConstruct
    void postConstruct() {
        // Reflections 扫描所有注解并根据 protocolId 和 order 排序
        Set<Method> methods = reflections.getMethodsAnnotatedWith(ResponseDecodeExtension.class);
        for (Method m : methods) {
            MethodInfo info;
            ResponseDecodeExtension ex = m.getAnnotation(ResponseDecodeExtension.class);
            String protocolId = ex.protocolId();
            Integer order = ex.order();
            
            // 判断 method 是否为单参数接受 JsonNode 的方法
            Class<?>[] parameterTypes = m.getParameterTypes();
            Class<?> declaringClass = m.getDeclaringClass();
            if (parameterTypes.length != 1 || parameterTypes[0] != JsonNode.class) {
                log.warn("方法 {}#{} 不为类型为 JsonNode 的单参数方法，暂不支持作为解码额外选项",
                        declaringClass.getSimpleName(), m.getName(), declaringClass.getSimpleName());
                continue;
            }
            
            // 判断 method 是否是静态
            if (Modifier.isStatic(m.getModifiers())) {
                info = new MethodInfo(m, null, order);
            }
            else {
                if (!SpringBeanDetector.isSpringManagedClass(declaringClass)) {
                    log.warn("方法 {} 所属类 {} 不归属于 Spring 管理，目前暂不支持作为解码额外选项",
                            m.getName(), declaringClass.getSimpleName());
                    continue;
                }
                info = new MethodInfo(m, declaringClass, order);
            }
            info.setClient(ex.client());
            List<MethodInfo> list = responseDecodeExtensions.get(protocolId);
            if (list == null) {
                list = new ArrayList<>();
                list.add(info);
                responseDecodeExtensions.put(protocolId, list);
            }
            else {
                list.add(info);
            }
        }
        for (List<MethodInfo> list : responseDecodeExtensions.values()) {
            list.sort(Comparator.comparingInt(info -> info.getOrder()));
        }
        log.debug("RequestResponseInspectService: 共载入 {} 个 ProtocolID 的 {} 个方法", 
                responseDecodeExtensions.keySet().size(), responseDecodeExtensions.values().size());
    }
    
    /**
     * 对 Response 进行 inspect
     * @param protocolId
     * @param nano
     * @param fromClient 该 inspect 来源是否是 client. 
     * @return
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     */
    public <U extends MessageNano> void inspectResponse(String protocolId, U nano, boolean fromClient) throws IllegalAccessException, InvocationTargetException {
        // 查找 ResponseDecodeExtension
        List<MethodInfo> methodInfos = responseDecodeExtensions.get(protocolId.toString());
        JsonNode jo = objectMapper.valueToTree(nano);
        if (methodInfos != null) {
            for (MethodInfo methodInfo : methodInfos) {
                if (fromClient && !methodInfo.isClient()) {
                    // 来自客户端请求但该方法未开启客户端 inspect, 忽略
                    continue;
                }
                if (methodInfo.getInstance() != null) {
                    // instance 不为 null 则说明是已经取到的 spring bean, 直接调用
                    methodInfo.getMethod().invoke(methodInfo.getInstance(), jo);
                }
                else if (methodInfo.getDeclaringClass() != null) {
                    // 获取 spring 管理的实例类
                    Object instance = SpringContextHolder.getBean(methodInfo.getDeclaringClass());
                    methodInfo.setInstance(instance);
                    methodInfo.getMethod().invoke(instance, jo);
                }
                else {
                    // 静态方法直接 invoke
                    methodInfo.getMethod().invoke(null, jo);
                }
            }
        }
    }
}
