package com.inspect.job.util;

import com.inspect.base.core.utils.SpringUtils;
import com.inspect.base.core.utils.StringUtils;
import com.inspect.job.domain.SysJob;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class JobInvokeUtil {
    public static void invokeMethod(SysJob sysJob) throws Exception {
        String invokeTarget = sysJob.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);
        Object bean;
        if (!isValidClassName(beanName)) {
            bean = SpringUtils.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        } else {
            bean = Class.forName(beanName).newInstance();
            invokeMethod(bean, methodName, methodParams);
        }

    }

    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method;
        if (StringUtils.isNotNull(methodParams) && !methodParams.isEmpty()) {
            method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        } else {
            method = bean.getClass().getDeclaredMethod(methodName);
            method.invoke(bean);
        }

    }

    public static boolean isValidClassName(String invokeTarget) {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }

    public static String getBeanName(String invokeTarget) {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBeforeLast(beanName, ".");
    }

    public static String getMethodName(String invokeTarget) {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }

    public static List<Object[]> getMethodParams(String invokeTarget) {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
        if (StringUtils.isEmpty(methodStr)) {
            return null;
        } else {
            String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
            List<Object[]> objList = new LinkedList<>();

            for (String methodParam : methodParams) {
                String str = StringUtils.trimToEmpty(methodParam);
                if (StringUtils.startsWithAny(str, "'", "\"")) {
                    objList.add(new Object[]{StringUtils.substring(str, 1, str.length() - 1), String.class});
                } else if (!"true".equalsIgnoreCase(str) && !"false".equalsIgnoreCase(str)) {
                    if (StringUtils.endsWith(str, "L")) {
                        objList.add(new Object[]{Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class});
                    } else if (StringUtils.endsWith(str, "D")) {
                        objList.add(new Object[]{Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class});
                    } else {
                        objList.add(new Object[]{Integer.valueOf(str), Integer.class});
                    }
                } else {
                    objList.add(new Object[]{Boolean.valueOf(str), Boolean.class});
                }
            }

            return objList;
        }
    }

    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams) {
        Class<?>[] classes = new Class[methodParams.size()];
        int index = 0;

        for (Iterator<Object[]> iterator = methodParams.iterator(); iterator.hasNext(); ++index) {
            Object[] os = iterator.next();
            classes[index] = (Class) os[1];
        }

        return classes;
    }

    public static Object[] getMethodParamsValue(List<Object[]> methodParams) {
        Object[] objects = new Object[methodParams.size()];
        int index = 0;

        for (Iterator<Object[]> iterator = methodParams.iterator(); iterator.hasNext(); ++index) {
            Object[] os = iterator.next();
            objects[index] = os[0];
        }

        return objects;
    }
}
