package cc.smtweb.framework.core.mvc.controller.scan;

import cc.smtweb.framework.core.annotation.SwBean;
import cc.smtweb.framework.core.annotation.SwConstruct;
import cc.smtweb.framework.core.annotation.SwDestroy;
import cc.smtweb.framework.core.annotation.SwScheduling;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.mvc.controller.access.MethodAccess;
import cc.smtweb.framework.core.mvc.controller.access.SchedulerMethodAccess;
import cc.smtweb.framework.core.mvc.controller.binder.WebDataBinder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

@Getter
@Slf4j
public class ScanContext {
    private final BeanManager beanManager;
    private final WebDataBinder webDataBinder;
    //  private Map<Class<?>, List<IMethodAccess>> actions = new HashMap<>();
    private Map<Class<?>, BiFunction<Object, MethodAccess, Boolean>> dealMethodAnnotation = new HashMap<>();
    @Setter
    private String module;

    public ScanContext(BeanManager beanManager, WebDataBinder webDataBinder) {
        this.beanManager = beanManager;
        this.webDataBinder = webDataBinder;
//    if (scheduling != null) {
//      // 定时任务用seed值限定
//      log.debug("  add timer task: " + clazz.getSimpleName() + "." + m.getName());
//      controllers.addTask(new SchedulerMethodAccess(methodAccess, scheduling));
//    } else if(construct != null) {
//      controllers.addConstruct(construct.order(), methodAccess);
//    } else if(destroy != null) {
//      controllers.addDestroy(destroy.order(), methodAccess);
//    } else if(bean != null) {
//      controllers.addBeanMethod(methodAccess);
//    } else if(action != null) {
//      controllers.addAction(action, methodAccess);
//    } else if (isApi) {
//      urlMaker.addApi(clazz, m, methodAccess, controllers);
//    }
        dealMethodAnnotation.put(SwScheduling.class, (a, methodAccess) -> {
            try {
                // 定时任务用seed值限定
                beanManager.addTask(new SchedulerMethodAccess(methodAccess, ((SwScheduling) a)));
                log.debug("  add timer task: " + methodAccess.fullName());
            } catch (ParseException e) {
                throw new SwException(e);
            }

            return false;
        });

        dealMethodAnnotation.put(SwConstruct.class, (a, methodAccess) -> {
            beanManager.addConstruct(((SwConstruct) a).order(), methodAccess);
            return false;
        });

        dealMethodAnnotation.put(SwDestroy.class, (a, methodAccess) -> {
            beanManager.addDestroy(((SwDestroy) a).order(), methodAccess);
            return false;
        });

        dealMethodAnnotation.put(SwBean.class, (a, methodAccess) -> {
            beanManager.addBeanMethod(methodAccess);
            return false;
        });
//
//    dealMethodAnnotation.put(SwAction.class, (a, methodAccess) -> {
//      return true;
//    });
//
//    dealMethodAnnotation.put(SwPerm.class, (a, methodAccess) -> {
//      return true;
//    });
    }

    public void addScanAction(IScanAction apiLoader) {
        Object old = dealMethodAnnotation.putIfAbsent(apiLoader.actionType(), (a, methodAccess) -> {
            apiLoader.load(beanManager, module, a, methodAccess);
            return false;
        });

        if (old != null) {
            log.error("已经存在动作解析器：" + apiLoader.actionType());
        }
    }

    public boolean dealMethod(Method m, MethodAccess methodAccess) {
        boolean result = true;
        Annotation[] annos = m.getDeclaredAnnotations();
        if (annos != null) {
            for (Annotation anno : annos) {
                BiFunction<Object, MethodAccess, Boolean> func = dealMethodAnnotation.get(anno.annotationType());
                if (func != null) {
                    boolean ret = func.apply(anno, methodAccess);
                    if (!ret) {
                        result = false;
                        break;
                    }
                }
            }
        }

        return result;
    }
}
