package com.pictc.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.springframework.context.EnvironmentAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import com.pictc.cache.CacheFactory;
import com.pictc.cache.TanukiCache;

/**
 * @Description TODO(这里用一句话描述这个类的作用)
 * @author zhangfucai
 * @Date 2022年11月6日 上午10:22:31
 * @version 1.0.0
 */
@Component
public class SpringAnnotationUtils implements EnvironmentAware{
	
	private static final TanukiCache<String,InvocationHandler> handlerCache= CacheFactory.factory("Invocation-cache");
	
	private static final TanukiCache<String,Annotation> findCache = CacheFactory.factory("Annotation-cache");
	
	private static final String  findKeyFormat="%s@%s";
	
	private static final String  handlerKeyFormat="%s@%s";
	
	private static Environment environment;
	 
	@SuppressWarnings("unchecked")
	public static <A extends Annotation>A findAnnotation(Class<?> clazz,Class<A> annotationType){
		if(!clazz.isAnnotationPresent(annotationType)) {
			return null;
		}
		String key=String.format(findKeyFormat, clazz.getName(),annotationType.getName());
		if(!findCache.containsKey(key)) {
			findCache.save(key, resolveAnnotationEnvValue(clazz.getAnnotation(annotationType)));
		}
		return (A) findCache.get(key);
	}
	
	@SuppressWarnings("unchecked")
	public static <A extends Annotation>A findAnnotation(AnnotatedElement element,Class<A> annotationType){
		if(!element.isAnnotationPresent(annotationType)) {
			return null;
		}
		String key=String.format(findKeyFormat,System.identityHashCode(element),annotationType.getName());
		if(!findCache.containsKey(key)) {
			findCache.save(key, resolveAnnotationEnvValue(element.getAnnotation(annotationType)));
		}
		return (A) findCache.get(key);
	}

	private static <A extends Annotation>A resolveAnnotationEnvValue(A annotation) {
		Map<String, Object>	values=AnnotationUtils.getAnnotationAttributes(annotation);
		if(values!=null) {
			Set<Entry<String, Object>> vs= values.entrySet();
			for (Entry<String, Object> entry : vs) {
				values.put(entry.getKey(),parseValue(entry.getValue()));
			}
		}
		changeValues(annotation, values);
		return annotation;
	}
	
	@SuppressWarnings("unchecked")
	public static Map<String,Object> getValues(Annotation annotation){
		String key=String.format(handlerKeyFormat,annotation.getClass().getName(),System.identityHashCode(annotation));
		if(!handlerCache.containsKey(key)) {
			handlerCache.save(key, Proxy.getInvocationHandler(annotation));
		}
		InvocationHandler invocationHandler =handlerCache.get(key);
		try {
			Field declaredField = invocationHandler.getClass().getDeclaredField("memberValues");
			declaredField.setAccessible(true);
	        return (Map<String, Object>) declaredField.get(invocationHandler);
		} catch (Exception e) {}
		return null;
	}
	
	public static void changeValue(Annotation annotation,String name,Object value) {
		Map<String, Object> values = getValues(annotation);
		if(values!=null) {
			values.put(name,value);
		}
	}
	
	public static void changeValues(Annotation annotation,Map<String,Object> attrs) {
		Map<String, Object> values = getValues(annotation);
		if(values!=null) {
			values.putAll(attrs);
		}
	}
	
	
	private static Object parseValue(Object val) {
		if(environment==null) {
			return val;
		}
		if(val!=null && val instanceof String && RegexUtils.has(val.toString())) {
			String strVal=val.toString();
			List<String> names=RegexUtils.findPlaceholder(strVal);
			String[] values=new String[names.size()];
			for (int i = 0; i < values.length; i++) {
				String key=names.get(i);
				String[] keys=key.split(":");
				if(keys.length==2) {
					 if(environment.containsProperty(keys[0])) {
						values[i]=environment.getProperty(keys[0]);
					 }else {
						values[i]=keys[1];
					 }
				}else {
					values[i] = environment.getProperty(key);
				}
			}
			return RegexUtils.replacePlaceholder(strVal, values);
		}
		return val;
	}


	@Override
	public void setEnvironment(Environment environment) {
		SpringAnnotationUtils.environment=environment;
	}

}
