package com.ruoyi.security.aspect;

import com.ruoyi.security.annotation.EncryptedField;
import com.ruoyi.security.service.EncryptionService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Collection;

/**
 * 数据加密解密切面
 * 自动处理带有@EncryptedField注解的字段
 * 
 * @author ruoyi
 */
@Aspect
@Component
public class EncryptionAspect {
    
    private static final Logger log = LoggerFactory.getLogger(EncryptionAspect.class);
    
    @Autowired
    private EncryptionService encryptionService;
    
    /**
     * 拦截Mapper的insert和update方法，自动加密敏感字段
     */
    @Around("execution(* com.ruoyi.*.mapper.*Mapper.insert*(..)) || " +
            "execution(* com.ruoyi.*.mapper.*Mapper.update*(..))")
    public Object encryptBeforeSave(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        
        for (Object arg : args) {
            if (arg != null) {
                encryptFields(arg);
            }
        }
        
        return joinPoint.proceed(args);
    }
    
    /**
     * 拦截Mapper的select方法，自动解密敏感字段
     */
    @Around("execution(* com.ruoyi.*.mapper.*Mapper.select*(..)) || " +
            "execution(* com.ruoyi.*.mapper.*Mapper.find*(..)) || " +
            "execution(* com.ruoyi.*.mapper.*Mapper.get*(..))")
    public Object decryptAfterLoad(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        
        if (result != null) {
            if (result instanceof Collection) {
                Collection<?> collection = (Collection<?>) result;
                for (Object item : collection) {
                    decryptFields(item);
                }
            } else {
                decryptFields(result);
            }
        }
        
        return result;
    }
    
    /**
     * 加密对象中的敏感字段
     */
    private void encryptFields(Object obj) {
        if (obj == null) {
            return;
        }
        
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        
        for (Field field : fields) {
            if (field.isAnnotationPresent(EncryptedField.class)) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    
                    if (value instanceof String && !((String) value).isEmpty()) {
                        String encryptedValue = encryptionService.encrypt((String) value);
                        field.set(obj, encryptedValue);
                        log.debug("Encrypted field: {}.{}", clazz.getSimpleName(), field.getName());
                    }
                } catch (Exception e) {
                    log.error("Failed to encrypt field: {}.{}", clazz.getSimpleName(), field.getName(), e);
                }
            }
        }
    }
    
    /**
     * 解密对象中的敏感字段
     */
    private void decryptFields(Object obj) {
        if (obj == null) {
            return;
        }
        
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        
        for (Field field : fields) {
            if (field.isAnnotationPresent(EncryptedField.class)) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    
                    if (value instanceof String && !((String) value).isEmpty()) {
                        String decryptedValue = encryptionService.decrypt((String) value);
                        field.set(obj, decryptedValue);
                        log.debug("Decrypted field: {}.{}", clazz.getSimpleName(), field.getName());
                    }
                } catch (Exception e) {
                    log.error("Failed to decrypt field: {}.{}", clazz.getSimpleName(), field.getName(), e);
                }
            }
        }
    }
}