package cn.iocoder.yudao.module.bpm.framework.core.util;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 反射调用工具类
 * 用于动态调用Mapper方法，支持流程映射配置系统的动态查询功能
 *
 * @author 系统
 */
@Slf4j
public class ReflectionInvoker {

    /**
     * 动态调用Mapper方法
     *
     * @param mapperBean Spring容器中的Mapper实例
     * @param methodName 方法名(如"selectById")
     * @param paramType  参数类型
     * @param args       参数值
     * @return 方法执行结果
     */
    public static Object invokeMapperMethod(Object mapperBean, String methodName, Class<?> paramType, Object... args) {
        return invokeMapperMethod(mapperBean, methodName, new Class<?>[]{paramType}, args);
    }

    /**
     * 动态调用Mapper方法
     *
     * @param mapperBean Spring容器中的Mapper实例
     * @param methodName 方法名(如"selectById")
     * @param paramTypes 参数类型数组
     * @param args       参数值数组
     * @return 方法执行结果
     */
    public static Object invokeMapperMethod(Object mapperBean, String methodName, Class<?>[] paramTypes, Object... args) {
        if (mapperBean == null) {
            throw new IllegalArgumentException("mapperBean cannot be null");
        }
        if (methodName == null || methodName.trim().isEmpty()) {
            throw new IllegalArgumentException("methodName cannot be null or empty");
        }

        try {
            // 获取Mapper类
            Class<?> mapperClass = mapperBean.getClass();
            
            // 处理代理类的情况，获取实际的接口类型
            Class<?> targetClass = getTargetInterface(mapperClass);
            
            log.debug("[invokeMapperMethod][开始调用] class={}, method={}, paramTypes={}, args={}", 
                    targetClass.getSimpleName(), methodName, Arrays.toString(paramTypes), Arrays.toString(args));

            // 查找匹配的方法
            Method method = findMatchingMethod(targetClass, methodName, paramTypes);
            if (method == null) {
                throw new NoSuchMethodException(String.format("Method '%s' with parameter types %s not found in class %s", 
                        methodName, Arrays.toString(paramTypes), targetClass.getName()));
            }

            // 设置方法可访问
            method.setAccessible(true);

            // 调用方法
            Object result = method.invoke(mapperBean, args);
            
            log.debug("[invokeMapperMethod][调用成功] class={}, method={}, result={}", 
                    targetClass.getSimpleName(), methodName, result);
            
            return result;

        } catch (Exception e) {
            String errorMsg = String.format("Failed to invoke method '%s' on class '%s'", 
                    methodName, mapperBean.getClass().getName());
            log.error("[invokeMapperMethod][调用失败] {}, paramTypes={}, args={}", 
                    errorMsg, Arrays.toString(paramTypes), Arrays.toString(args), e);
            throw new RuntimeException(errorMsg, e);
        }
    }

    /**
     * 获取目标接口类型
     * 处理Spring代理类的情况
     */
    private static Class<?> getTargetInterface(Class<?> clazz) {
        // 如果是代理类，尝试获取实际的接口
        if (clazz.getName().contains("$Proxy") || clazz.getName().contains("CGLIB")) {
            Class<?>[] interfaces = clazz.getInterfaces();
            if (interfaces.length > 0) {
                // 选择第一个看起来像Mapper的接口
                for (Class<?> intf : interfaces) {
                    if (intf.getName().contains("Mapper")) {
                        return intf;
                    }
                }
                return interfaces[0];
            }
        }
        return clazz;
    }

    /**
     * 查找匹配的方法
     * 支持精确匹配和兼容性匹配
     */
    private static Method findMatchingMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes) {
        Method[] methods = clazz.getMethods();
        
        // 先尝试精确匹配
        for (Method method : methods) {
            if (method.getName().equals(methodName) && 
                Arrays.equals(method.getParameterTypes(), paramTypes)) {
                return method;
            }
        }

        // 再尝试兼容性匹配（参数类型可赋值）
        for (Method method : methods) {
            if (method.getName().equals(methodName) && 
                isParameterTypesAssignable(method.getParameterTypes(), paramTypes)) {
                return method;
            }
        }

        // 最后尝试参数数量匹配（用于处理泛型擦除的情况）
        for (Method method : methods) {
            if (method.getName().equals(methodName) && 
                method.getParameterCount() == paramTypes.length) {
                log.warn("[findMatchingMethod][使用参数数量匹配] method={}, expected={}, actual={}", 
                        method, Arrays.toString(paramTypes), Arrays.toString(method.getParameterTypes()));
                return method;
            }
        }

        return null;
    }

    /**
     * 检查参数类型是否可赋值
     */
    private static boolean isParameterTypesAssignable(Class<?>[] methodParams, Class<?>[] providedParams) {
        if (methodParams.length != providedParams.length) {
            return false;
        }

        for (int i = 0; i < methodParams.length; i++) {
            if (!isAssignable(methodParams[i], providedParams[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 检查类型是否可赋值（包含基本类型和包装类型的转换）
     */
    private static boolean isAssignable(Class<?> target, Class<?> source) {
        if (target.isAssignableFrom(source)) {
            return true;
        }

        // 处理基本类型和包装类型
        if (target.isPrimitive() || source.isPrimitive()) {
            return isPrimitiveAssignable(target, source);
        }

        return false;
    }

    /**
     * 检查基本类型是否可赋值
     */
    private static boolean isPrimitiveAssignable(Class<?> target, Class<?> source) {
        if (target == source) return true;
        
        // 常见的基本类型转换
        if ((target == int.class && source == Integer.class) ||
            (target == Integer.class && source == int.class) ||
            (target == long.class && source == Long.class) ||
            (target == Long.class && source == long.class) ||
            (target == boolean.class && source == Boolean.class) ||
            (target == Boolean.class && source == boolean.class)) {
            return true;
        }
        
        return false;
    }
} 