package com.cool.store.service.dict.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.lang.Pair;
import com.cool.store.annotation.DictField;
import com.cool.store.dao.dict.SysDictColumnDAO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 * 字典服务类
 * </p>
 *
 * @author wangff
 * @since 2025/11/4
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DictService {
    private final SysDictColumnDAO columnDAO;
    private final Map<Class<?>, List<Pair<Field, Field>>> dictFieldCache = new ConcurrentHashMap<>();
    private final Map<Class<?>, Field[]> fieldCache = new ConcurrentHashMap<>();

    /**
     * 填充字典字段
     */
    public <T> void fillDictField(T obj) {
        fillDictField(Collections.singletonList(obj));
    }

    /**
     * 填充字典字段
     */
    public <T> void fillDictField(List<T> objs) {
        if (CollectionUtils.isEmpty(objs)) {
            return;
        }
        Class<?> clazz = objs.get(0).getClass();
        fillDictField(objs, clazz, new HashSet<>());
    }

    private <T> void fillDictField(List<T> objs, Class<?> clazz, Set<Object> processedObjects) {
        List<Pair<Field, Field>> dictFields = getDictField(clazz);
        if (CollectionUtils.isEmpty(dictFields)) {
            return;
        }
        Set<String> columnCodes = new HashSet<>();
        List<T> validObjs = new ArrayList<>();
        for (T obj : objs) {
            if (obj == null || processedObjects.contains(obj)) continue;
            processedObjects.add(obj);
            validObjs.add(obj);
            for (Pair<Field, Field> dictField : dictFields) {
                try {
                    Object value = dictField.getValue().get(obj);
                    if (value != null) {
                        columnCodes.add((String) value);
                    }
                } catch (IllegalAccessException e) {
                    log.info("字典字段值获取失败", e);
                }
            }
        }
        if (!columnCodes.isEmpty()) {
            Map<String, String> nameMap = columnDAO.getNameMapIncludeDeleted(new ArrayList<>(columnCodes));
            for (T obj : objs) {
                for (Pair<Field, Field> dictField : dictFields) {
                    try {
                        dictField.getKey().set(obj, nameMap.get((String) dictField.getValue().get(obj)));
                    } catch (IllegalAccessException e) {
                        log.info("字典字段值填充失败", e);
                    }
                }
            }
        }
        processNestedObjects(validObjs, clazz, processedObjects);
    }

    private <T> void processNestedObjects(List<T> objs, Class<?> clazz, Set<Object> processedObjects) {
        Field[] fields = getFieldCache(clazz);
        for (Field field : fields) {
            if (isComplexObjectField(field)) {
                field.setAccessible(true);

                for (T obj : objs) {
                    if (obj == null) continue;

                    try {
                        Object fieldValue = field.get(obj);
                        if (fieldValue != null) {
                            if (fieldValue instanceof Collection) {
                                // 处理集合类型字段
                                Collection<?> collection = (Collection<?>) fieldValue;
                                List<Object> nestedObjs = new ArrayList<>();
                                for (Object item : collection) {
                                    if (item != null && isComplexObject(item.getClass())) {
                                        nestedObjs.add(item);
                                    }
                                }
                                if (!nestedObjs.isEmpty()) {
                                    fillDictField(nestedObjs, nestedObjs.get(0).getClass(), new HashSet<>(processedObjects));
                                }
                            } else if (isComplexObject(fieldValue.getClass())) {
                                // 处理单一对象类型字段
                                fillDictField(Collections.singletonList(fieldValue), fieldValue.getClass(), processedObjects);
                            }
                        }
                    } catch (IllegalAccessException e) {
                        log.info("嵌套对象字段获取失败", e);
                    }
                }
            }
        }
    }

    private boolean isComplexObjectField(Field field) {
        Class<?> fieldType = field.getType();
        return isComplexObject(fieldType);
    }

    private boolean isComplexObject(Class<?> clazz) {
        return !clazz.isPrimitive() &&
                !clazz.getName().startsWith("java") &&
                !clazz.equals(String.class);
    }

    private Field[] getFieldCache(Class<?> clazz) {
        return fieldCache.computeIfAbsent(clazz, this::getAllFields);
    }

    private Field[] getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null && !clazz.equals(Object.class)) {
            Collections.addAll(fields, clazz.getDeclaredFields());
            clazz = clazz.getSuperclass();
        }
        return fields.toArray(new Field[0]);
    }

    private <T> List<Pair<Field, Field>> getDictField(Class<T> clazz) {
        return dictFieldCache.computeIfAbsent(clazz, this::computeDictFields);
    }

    private <T> List<Pair<Field, Field>> computeDictFields(Class<T> clazz) {
        Field[] fields = getFieldCache(clazz);
        Map<String, Field> fieldMap = CollStreamUtil.toMap(Arrays.asList(fields), Field::getName, v -> v);
        List<Pair<Field, Field>> dictFields = new ArrayList<>();
        for (Field field : fields) {
            // 这里需要优化，对象类型字段也需要转义，注意可能出现的循环嵌套问题
            DictField dictField = field.getAnnotation(DictField.class);
            if (Objects.nonNull(dictField)) {
                field.setAccessible(true);
                String fieldName = field.getName();
                String sourceFieldName = StringUtils.isBlank(dictField.sourceField()) && fieldName.endsWith("Name")
                        ? fieldName.substring(0, fieldName.length() - 4)
                        : dictField.sourceField();
                if (StringUtils.isNotBlank(sourceFieldName) && fieldMap.containsKey(sourceFieldName)) {
                    Field sourceField = fieldMap.get(sourceFieldName);
                    sourceField.setAccessible(true);
                    dictFields.add(new Pair<>(field, sourceField));
                }
            }
        }
        return dictFields;
    }
}
