package com.brilliance.mda.runtime.mda.driver.compile;

import com.brilliance.mda.runtime.annotation.Check;
import com.brilliance.mda.runtime.annotation.Default;
import com.brilliance.mda.runtime.annotation.Init;
import com.brilliance.mda.runtime.annotation.Rule;
import com.brilliance.mda.runtime.mda.FieldHolder;
import com.brilliance.mda.runtime.mda.IModule;
import com.brilliance.mda.runtime.mda.IModuleList;
import com.brilliance.mda.runtime.mda.ModuleHolder;
import com.brilliance.mda.runtime.mda.driver.compile.component.FieldInfo;
import com.brilliance.mda.runtime.mda.driver.compile.component.RuleItem;
import com.brilliance.mda.runtime.mda.driver.compile.processor.ModuleAnnotationProcess;
import com.brilliance.mda.runtime.response.ResponseSet;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/model/view")
public class ModelViewer {

    private static Map<String, Object> cache = new ConcurrentHashMap<>();

    @GetMapping("/{modelClass}")
    public Object viewModelData(@PathVariable String modelClass) {
        //根据交易名获取数据结构列表
        try {
            if (cache.containsKey(modelClass)) {
                return ResponseSet.simpleSuccess(cache.get(modelClass));
            }
            Class modelClazz = Class.forName(modelClass);
            Object classDesc = outModelClazz(modelClazz);
            cache.put(modelClass, classDesc);
            return ResponseSet.simpleSuccess(classDesc);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseSet.fail("没有找到对应的模型");
    }

    private Map<String, Object> outModelClazz(Class modelClazz) {
        List<FieldInfo> fields = new ArrayList<>();
        Map<String, Object> methods = new LinkedHashMap<>();
        List<Method> skipLocals = new ArrayList<>();
        Arrays.stream(modelClazz.getDeclaredFields()).forEach(field -> {
            if (Modifier.isStatic(field.getModifiers())) {
                return;
            }
            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.setName(field.getName());
            fieldInfo.setTypeName(field.getType().getCanonicalName());

            if (IModuleList.class.isAssignableFrom(field.getType())) {
                fieldInfo.setType(2);
                fieldInfo.setTypeName(field.getType().getCanonicalName());
                if (field.getGenericType() instanceof ParameterizedTypeImpl) {
                    //取出泛型
                    String typeName = ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0].getTypeName();
                    fieldInfo.setTypeName(typeName);
                }
            } else if (IModule.class == field.getType()) {
                fieldInfo.setType(3);
                fieldInfo.setTypeName(field.getType().getCanonicalName());
            } else if (IModule.class.isAssignableFrom(field.getType())) {
                fieldInfo.setType(1);
                fieldInfo.setTypeName(field.getType().getCanonicalName());
            } else if (ModuleHolder.class.isAssignableFrom(field.getType())) {
                fieldInfo.setType(3);
                fieldInfo.setTypeName(field.getType().getCanonicalName());
                if (field.getGenericType() instanceof ParameterizedTypeImpl) {
                    //取出泛型
                    String typeName = ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0].getTypeName();
                    fieldInfo.setTypeName(typeName);
                }
            } else if (FieldHolder.class.isAssignableFrom(field.getType())) {
                fieldInfo.setType(4);
                fieldInfo.setTypeName(field.getType().getCanonicalName());
                if (field.getGenericType() instanceof ParameterizedTypeImpl) {
                    //取出泛型
                    String typeName = ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0].getTypeName();
                    fieldInfo.setTypeName(typeName);
                }
            } else {
                fieldInfo.setTypeName(field.getType().getCanonicalName());
            }
            fields.add(fieldInfo);
            //找到get,set 方法
            Method setter = ModuleAnnotationProcess.getSetterMethod(field);
            Method getter = ModuleAnnotationProcess.getGetterMethod(field);
            if (setter != null) {
                skipLocals.add(setter);
            }
            if (getter != null) {
                skipLocals.add(getter);
            }
        });
        List<Object> initList = new ArrayList<>();
        List<Object> checkList = new ArrayList<>();
        List<Object> localList = new ArrayList<>();
        List<Object> defaultList = new ArrayList<>();
        List<Object> staticList = new ArrayList<>();
        List<Object> ruleList = new ArrayList<>();
        List<Object> globalList = new ArrayList<>();

        methods.put("initList", initList);
        methods.put("localList", localList);
        methods.put("checkList", checkList);
        methods.put("defaultList", defaultList);
        methods.put("staticList", staticList);
        methods.put("ruleList", ruleList);
        methods.put("globalList", globalList);


        Arrays.stream(modelClazz.getDeclaredMethods()).forEach(method -> {
            String target = null;
            String belongPath = "";
            int order = 0;
            //屏蔽特殊方法
            if ((method.getName().equals("setArgument") && method.getParameters().length == 0) || method.getName().startsWith("lambda$")) {
                return;
            }
            if (method.isAnnotationPresent(Rule.class)) {
                Rule rule = method.getAnnotation(Rule.class);
                target = rule.target();
                order = rule.order();
                String[] multiTarget = rule.value();
                if (multiTarget == null || multiTarget.length == 0) {
                    multiTarget = new String[]{target};
                }
                buildRuleItem(ruleList, RuleItem.RULE, multiTarget, method, order);
            } else if (method.isAnnotationPresent(Check.class)) {
                Check check = method.getAnnotation(Check.class);
                target = check.target();
                order = check.order();
                String[] multiTarget = check.value();
                if (multiTarget == null || multiTarget.length == 0) {
                    multiTarget = new String[]{target};
                }
                buildRuleItem(checkList, RuleItem.CHECK, multiTarget, method, order);
            } else if (method.isAnnotationPresent(Default.class)) {
                Default defaultAno = method.getAnnotation(Default.class);
                target = defaultAno.target();
                order = defaultAno.order();
                String[] multiTarget = defaultAno.value();
                if (multiTarget == null || multiTarget.length == 0) {
                    multiTarget = new String[]{target};
                }
                buildRuleItem(defaultList, RuleItem.DEFAULT, multiTarget, method, order);
            } else if (method.isAnnotationPresent(Init.class)) {
                Init init = method.getAnnotation(Init.class);
                order = init.order();
                buildRuleItem(initList, RuleItem.INIT, new String[0], method, order);
            } else if (Modifier.isStatic(method.getModifiers())) {
                buildRuleItem(globalList, RuleItem.GLOBAL, new String[0], method, order);
            } else if (!skipLocals.contains(method)) {
                buildRuleItem(localList, RuleItem.LOCAL, new String[0], method, order);
            }
        });
        Map<String, Object> descriptions = new LinkedHashMap<>();
        descriptions.put("fields", fields);
        descriptions.put("rules", methods);
        return descriptions;
    }

    private void buildRuleItem(List<Object> seriesRule, String ruleType, String[] multiTarget, Method method, int order) {
        RuleItem ruleItem = new RuleItem();
        ruleItem.setRuleType(ruleType);
        ruleItem.setOrder(order);
        ruleItem.setMethod(method);
        ruleItem.setDotPath(Arrays.stream(multiTarget).reduce(new StringJoiner(","), (s, i) -> s.add(i), (a, b) -> a.add(b.toString())).toString());
        seriesRule.add(ruleItem);
    }

    @GetMapping("/all")
    public Object viewAllTrans() {
        //根据交易名获取数据结构列表
        List<String> rs = ModuleAnnotationProcess.cache.entrySet().stream().filter(entry -> {
            return entry.getValue().isTrans();
        }).map(entry -> entry.getKey()).collect(Collectors.toList());
        return ResponseSet.simpleSuccess(rs);
    }
}
