package com.qmth.boot.api.config;

import com.qmth.boot.api.annotation.Aac;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.HandlerMethod;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Component
public class ApiConfigService implements ApplicationListener<ContextRefreshedEvent> {

    protected static final Logger log = LoggerFactory.getLogger(ApiConfigService.class);

    private ApiProperties apiProperties;

    private Map<Method, ApiConfig> configMap;

    public ApiConfigService(ApiProperties apiProperties) {
        this.apiProperties = apiProperties;
        this.configMap = new HashMap<>();
    }

    public ApiConfig getApiConfig(HandlerMethod handlerMethod) {
        return configMap.get(handlerMethod.getMethod());
    }

    @Override
    public synchronized void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) {
            Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(RestController.class);
            for (Object bean : beans.values()) {
                init(bean);
            }
            beans = event.getApplicationContext().getBeansWithAnnotation(Controller.class);
            for (Object bean : beans.values()) {
                init(bean);
            }
            log.info("ApiConfigService inited, aac method count={}", configMap.size());
        }
    }

    private void init(Object bean) {
        Class<?> clazz = AopUtils.getTargetClass(bean);
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (filter(method)) {
                initMethod(clazz, method);
            }
        }
    }

    private void initMethod(Class<?> clazz, Method method) {
        ApiConfig ac = new ApiConfig(apiProperties);
        ac.merge(new ApiConfig(AnnotationUtils.findAnnotation(clazz, Aac.class)));
        ac.merge(new ApiConfig(AnnotationUtils.findAnnotation(method, Aac.class)));
        configMap.put(method, ac);
        log.debug("Api method inited, {}:{}", clazz.getName(), method.getName());
    }

    private boolean filter(Method method) {
        return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null
                || AnnotationUtils.findAnnotation(method, GetMapping.class) != null
                || AnnotationUtils.findAnnotation(method, PostMapping.class) != null
                || AnnotationUtils.findAnnotation(method, PutMapping.class) != null
                || AnnotationUtils.findAnnotation(method, DeleteMapping.class) != null;
    }
}
