package com.aizuda.snailjob.client.common.rpc.supports.scan;

import com.aizuda.snailjob.client.common.annotation.Mapping;
import com.aizuda.snailjob.client.common.annotation.SnailEndPoint;
import com.aizuda.snailjob.common.log.SnailJobLog;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author opensnail
 * @date 2024-04-11 22:29:07
 * @since 3.3.0
 */
@Component
public class SnailEndPointScanner implements ApplicationContextAware {

    private ApplicationContext context;

    public List<EndPointInfo> doScan() {
        return scanEndPoint();
    }

    private List<EndPointInfo> scanEndPoint() {

        List<EndPointInfo> endPointInfoList = new ArrayList<>();
        String[] beanDefinitionNames = context.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = context.getBean(beanDefinitionName);
            Class executorNotProxy = AopProxyUtils.ultimateTargetClass(bean);
            String executorClassName = executorNotProxy.getName();

            // 扫描类的注解
            SnailEndPoint jobExecutor = bean.getClass().getAnnotation(SnailEndPoint.class);
            if (Objects.nonNull(jobExecutor)) {
                Map<Method, Mapping> annotatedMethods = null;
                try {
                    annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                            (MethodIntrospector.MetadataLookup<Mapping>) method -> AnnotatedElementUtils
                                    .findMergedAnnotation(method, Mapping.class));
                } catch (Throwable ex) {
                    SnailJobLog.LOCAL.error("{} Mapping加载异常：{}", beanDefinitionName, ex);
                }

                for (Map.Entry<Method, Mapping> entry : annotatedMethods.entrySet()) {
                    Method method = entry.getKey();
                    Mapping mapping = entry.getValue();
                    endPointInfoList.add(EndPointInfo.builder()
                            .executorName(executorClassName)
                            .method(method)
                            .executor(bean)
                            .path(mapping.path())
                            .requestMethod(mapping.method())
                            .build());
                }
            }

        }

        return endPointInfoList;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }
}
