package com.yonge.cooleshow.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.beans.Introspector;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * 自定义 组件命名规则 返回全限定名称
 *
 */
@Slf4j
public class AppBeanNameGenerator implements BeanNameGenerator {

    /**
     * 自定义 组件命名规则 返回全限定名称
     * @param definition BeanDefinition
     * @param registry BeanDefinitionRegistry
     * @return String
     */
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {

        //获取全限定名称
        String beanClassName = buildDefaultBeanName(definition, registry);
        // 默认使用注解自定义名称
        return Optional.ofNullable(determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition)).orElse(beanClassName);
    }

    protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
        AnnotationMetadata amd = annotatedDef.getMetadata();
        Set<String> types = amd.getAnnotationTypes();
        String beanName = null;

        for (String type : types) {
            AnnotationAttributes attributes = attributesFor(amd, type);
            if (attributes != null && this.isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
                Object value = attributes.get("value");
                if (value instanceof String) {
                    String strVal = (String) value;
                    if (StringUtils.hasLength(strVal)) {
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent component names: '" + beanName + "' versus '" + strVal + "'");
                        }

                        beanName = strVal;
                    }
                }
            }
        }

        return beanName;
    }

    static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, String annotationClassName) {
        return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName, false));
    }

    protected boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {
        boolean isStereotype = annotationType.equals("org.springframework.stereotype.Component") || metaAnnotationTypes.contains("org.springframework.stereotype.Component") || annotationType.equals("javax.annotation.ManagedBean") || annotationType.equals("javax.inject.Named");
        return isStereotype && attributes != null && attributes.containsKey("value");
    }

    protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return this.buildDefaultBeanName(definition);
    }

    protected String buildDefaultBeanName(BeanDefinition definition) {
        //获取全限定名称
        String beanClassName = definition.getBeanClassName();
        Assert.state(beanClassName != null, "No bean class name set");
        //获取类名
        String shortClassName = ClassUtils.getShortName(beanClassName);
        //小写首字母
        String decapitalizetalize = Introspector.decapitalize(shortClassName);
        log.info("beanClassName:{}, shortClassName={}, decapitalizetalize={}", beanClassName, shortClassName, decapitalizetalize);

        // 正则匹配路beanClassName包含controller
        if (beanClassName.matches(".*controller.*") || beanClassName.matches(".*task.*") || shortClassName.endsWith("Controller")) {
            return beanClassName;
        }
        // 返回小写首字母
        return decapitalizetalize;
    }
}
