package link.at17.mid.tushare.service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.reflections.Reflections;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.PostConstruct;
import link.at17.mid.tushare.annotation.UpdateMethod;
import link.at17.mid.tushare.data.models.UpdateMethodInfo;
import link.at17.mid.tushare.data.models.UpdateMethodInfo.UpdateParamInfo;
import link.at17.mid.tushare.data.validator.AllowedEnum;
import link.at17.mid.tushare.system.util.SpringBeanDetector;
import link.at17.mid.tushare.system.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;


@Service
@Slf4j
@Lazy(false)
@Validated
public class UpdateMethodService {
    
    @Autowired
    Reflections reflections;
    
    /**
     * 存放初始化 UpdateMethodInfo 的缓存，主要用于通过 id 来找到原始记录
     */
    Map<String, UpdateMethodInfo> updateMethodInfoCaches = new LinkedHashMap<>();
    
    @PostConstruct
    void postConstruct() {
        
        // 从 reflections 拿信息
        List<Method> updateMethods = 
                new ArrayList<>(reflections.getMethodsAnnotatedWith(UpdateMethod.class));
        updateMethods.sort(Comparator.comparingInt(m -> m.getAnnotation(UpdateMethod.class).order()));
        
        methodLabel:
        for (Method method : updateMethods) {
            
            UpdateMethodInfo info = new UpdateMethodInfo();
            
            // 获取 UpdateMethod 注解基本信息
            UpdateMethod um = method.getAnnotation(UpdateMethod.class);
            String name = um.name();
            info.setName(name);
            info.setMethodName(method.getName());
            Class<?> declaringClass = method.getDeclaringClass();
            info.setDeclaringClassName(declaringClass.getName());
            
            // 判断该 Class 是否交由 Spring 管理
            boolean managedBySpring = SpringBeanDetector.isSpringManagedClass(declaringClass);
            if (!managedBySpring) {
                // TODO: 非 Spring 管理类的成员方法 / 静态方法的数据更新？
                log.warn("方法 {} 所属类 {} 不归属于 Spring 管理，目前暂不支持作为更新服务候选项",
                        method.getName(), declaringClass.getSimpleName());
                continue;
            }
            
            // 获取该方法的所有参数并读取
            List<UpdateMethodInfo.UpdateParamInfo> paramInfos = new ArrayList<>();
            for (Parameter p : method.getParameters()) {
                
                UpdateMethodInfo.UpdateParamInfo updateParamInfo = new UpdateMethodInfo.UpdateParamInfo();
                updateParamInfo.setName(p.getName());
                Class<?> parameterType = p.getType();
                updateParamInfo.setFullTypeName(parameterType.getName());
                updateParamInfo.setTypeName(parameterType.getSimpleName());
                updateParamInfo.setTypeClass(parameterType);
                
                if (parameterType.isEnum()) {
                    
                    // 当前枚举下的所有枚举值
                    List<String> allEnums = Arrays.stream(parameterType.getEnumConstants())
                            .map(Object::toString)
                            .toList();
                    
                    AllowedEnum allowedEnum = p.getAnnotation(AllowedEnum.class);
                    if (allowedEnum == null) {
                        // 未指定 AllowedEnum，将所有枚举放到 allowedEnumValues 里
                        updateParamInfo.setAllowedEnumValues(allEnums);
                    }
                    else {
                        // 指定了 AllowedEnum，将允许的枚举放到 allowedEnumValues 里
                        String[] allowedEnums = allowedEnum.value();
                        List<String> allowedEnumValues = new ArrayList<>();
                        for (String specificEnum : allowedEnums) {
                            if (allEnums.contains(specificEnum)) {
                                allowedEnumValues.add(specificEnum);
                            }
                            else {
                                log.warn("枚举类 {} 不存在指定的枚举值 {}，将忽略", parameterType.getSimpleName(), specificEnum);
                            }
                        }
                        updateParamInfo.setAllowedEnumValues(allowedEnumValues);
                    }
                    paramInfos.add(updateParamInfo);
                    continue;
                }
                else {
                    log.warn("方法 {} 参数 {} 非枚举类，目前暂不支持");
                    continue methodLabel;
                }
            }
            info.setParams(paramInfos);
            updateMethodInfoCaches.put(info.getId(), info);
        }
    }
    
    /**
     * 获取潜在的数据更新方法。这些方法通过反射扫描得来
     * @return
     */
    
    public List<UpdateMethodInfo> getPotentialUpdateMethodInfos() {
        return new ArrayList<>(this.updateMethodInfoCaches.values());
    }
    
    /**
     * 根据 UpdateMethodInfo 的 id 查找原始方法模板
     * @param id
     * @return
     */
    public UpdateMethodInfo findById(String id) {
        return this.updateMethodInfoCaches.get(id);
    }
    
    /**
     * 填充从外部来的 UpdateMethodInfo
     * <p>
     * 注意这里的 incoming 必须是通过 @Valid 校验的
     * @param incoming
     */
    public void fillUpdateMethodInfo(@Validated UpdateMethodInfo incoming) {
        // 填充 method 信息
        UpdateMethodInfo valid = findById(incoming.getId());
        BeanUtils.copyProperties(valid, incoming, "params");
        if (valid.getParams() == null || valid.getParams().size() == 0) return;
        for (int i = 0; i < valid.getParams().size(); i++) {
            UpdateParamInfo in = incoming.getParams().get(i);
            UpdateParamInfo ref = valid.getParams().get(i);
            BeanUtils.copyProperties(ref, in, "value");
        }
    }
    
    /**
     * 执行方法
     * @param updateMethodInfo
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     * @throws ClassNotFoundException 
     */
    @SuppressWarnings("unchecked")
    public void execute(@Validated UpdateMethodInfo updateMethodInfo) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
        Class<?> declaringClass = Class.forName(updateMethodInfo.getDeclaringClassName());
        Object declaringClassInstance = SpringContextHolder.getBean(declaringClass);
        boolean hasParams = !updateMethodInfo.getParams().isEmpty();
        Method method;
        
        if (hasParams) {
            @SuppressWarnings("rawtypes")
            Class[] paramTypes = new Class<?>[updateMethodInfo.getParams().size()];
            Object[] paramValues = new Object[updateMethodInfo.getParams().size()];
            for (int i = 0; i < updateMethodInfo.getParams().size(); i++) {
                UpdateParamInfo param = updateMethodInfo.getParams().get(i);
                paramTypes[i] = Class.forName(param.getFullTypeName());
                // 转换 parameterValue
                paramValues[i] = Enum.valueOf(paramTypes[i], param.getValue().toString());
            }
            method = declaringClass.getDeclaredMethod(updateMethodInfo.getMethodName(), paramTypes);
            method.invoke(declaringClassInstance, paramValues);
        }
        else {
            method = declaringClass.getDeclaredMethod(updateMethodInfo.getMethodName());
            method.invoke(declaringClassInstance);
        }
    }

}
