package org.dromara.system.zwy.service;


import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.zwy.annotation.EncryptSensitive;
import org.dromara.system.zwy.client.DecryptClient;
import org.dromara.system.zwy.client.EncryptClient;
import org.springframework.stereotype.Service;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class SensitiveDataService {

    private final EncryptClient encryptClient;
    private final DecryptClient decryptClient;


    /**
     * 对对象中的字段加密
     */
    public void encryptSensitiveFields(Object obj) {
        process(obj, true, new HashSet<>());
    }

    /**
     * 对对象中的字段解密
     */
    public void decryptSensitiveFields(Object obj) {
        process(obj, false, new HashSet<>());
    }

    /**
     * 递归处理对象中的字段
     */
    private void process(Object obj, boolean encrypt, Set<Object> visited) {
        if (obj == null || isPrimitiveOrWrapper(obj.getClass()) || visited.contains(obj)) {
            return;
        }

        visited.add(obj);

        if (obj instanceof Collection<?> collection) {
            for (Object item : collection) {
                process(item, encrypt, visited);
            }
            return;
        }

        if (obj instanceof Map<?, ?> map) {
            for (Object value : map.values()) {
                process(value, encrypt, visited);
            }
            return;
        }

        if (obj.getClass().isArray()) {
            for (int i = 0; i < Array.getLength(obj); i++) {
                process(Array.get(obj, i), encrypt, visited);
            }
            return;
        }

        for (Field field : getAllFields(obj.getClass())) {
            // 跳过JDK内部类或系统类字段，避免反射异常
            if (field.getDeclaringClass().getPackageName().startsWith("java.")) {
                continue;
            }
            try {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value == null) continue;

                if (field.isAnnotationPresent(EncryptSensitive.class)) {
                    if (value instanceof String strVal && !strVal.isEmpty()) {
                        String result = encrypt ? encryptClient.encrypt(strVal) : decryptClient.decrypt(strVal);
                        field.set(obj, result);
                    }
                } else {
                    process(value, encrypt, visited);
                }
            } catch (Exception e) {
                log.warn("Sensitive field [{}] {} failed", field.getName(), encrypt ? "encrypt" : "decrypt", e);
            }
        }
    }


    /**
     * 获取类及父类的所有字段
     */
    private List<Field> getAllFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null && !clazz.equals(Object.class)) {
            fieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }

    /**
     * 判断是否是原始类型或其包装类/枚举/字符串
     */
    private boolean isPrimitiveOrWrapper(Class<?> clazz) {
        return clazz.isPrimitive() ||
            clazz.isEnum() ||
            clazz == String.class ||
            clazz == Boolean.class ||
            clazz == Integer.class ||
            clazz == Long.class ||
            clazz == Double.class ||
            clazz == Float.class ||
            clazz == Short.class ||
            clazz == Byte.class ||
            clazz == Character.class ||
            Number.class.isAssignableFrom(clazz) ||
            clazz.getName().startsWith("java.time.");
    }
}
