package cn.novalon.manage.sys.audit;

import cn.novalon.manage.sys.audit.domain.AuditLog;
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
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.data.domain.Persistable;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * 审计日志切面
 * 
 * 文件定义：使用AOP自动拦截Repository操作，记录审计日志
 * 涉及业务：自动记录所有数据变更操作，包括变更前后对比
 * 算法：使用异步方式记录日志，不阻塞主流程
 * 
 * @author 张翔
 * @date 2026-04-01
 */
@Aspect
@Component
public class AuditLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(AuditLogAspect.class);

    private final IAuditLogRepository auditLogRepository;
    private final ObjectMapper objectMapper;

    public AuditLogAspect(IAuditLogRepository auditLogRepository, ObjectMapper objectMapper) {
        this.auditLogRepository = auditLogRepository;
        this.objectMapper = objectMapper;
    }

    @Around("execution(* cn.novalon.manage.db.repository.*Repository.save(..)) || " +
            "execution(* cn.novalon.manage.db.repository.*Repository.delete(..)) || " +
            "execution(* cn.novalon.manage.db.repository.*Repository.deleteById(..))")
    public Object logAuditEvent(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        Object[] args = joinPoint.getArgs();
        
        String operationType = determineOperationType(methodName);
        String entityType = extractEntityType(className);
        
        logger.debug("拦截审计操作: {}.{}, 操作类型: {}, 实体类型: {}", 
                    className, methodName, operationType, entityType);
        
        try {
            if ("save".equals(methodName) && args.length > 0) {
                return handleSaveOperation(joinPoint, args[0], entityType, operationType);
            } else if ("delete".equals(methodName) || "deleteById".equals(methodName)) {
                return handleDeleteOperation(joinPoint, args, entityType, operationType);
            }
            
            return joinPoint.proceed();
        } catch (Throwable error) {
            logger.error("审计日志记录失败: {}", error.getMessage(), error);
            throw error;
        }
    }

    private Object handleSaveOperation(ProceedingJoinPoint joinPoint, Object entity, 
                                       String entityType, String operationType) throws Throwable {
        try {
            final String[] beforeDataHolder = {null};
            final Long[] entityIdHolder = {null};
            final String[] operationTypeHolder = {operationType};
            
            if (entity instanceof Persistable) {
                Persistable<?> persistable = (Persistable<?>) entity;
                entityIdHolder[0] = persistable.getId() != null ? 
                          ((Number) persistable.getId()).longValue() : null;
                
                if (entityIdHolder[0] != null) {
                    beforeDataHolder[0] = fetchEntityBeforeData(entityType, entityIdHolder[0]);
                    operationTypeHolder[0] = "UPDATE";
                } else {
                    operationTypeHolder[0] = "CREATE";
                }
            }
            
            Object result = joinPoint.proceed();
            
            if (result instanceof Mono) {
                return ((Mono<?>) result).flatMap(savedEntity -> {
                    String afterData = serializeEntity(savedEntity);
                    Long finalEntityId = entityIdHolder[0] != null ? entityIdHolder[0] : extractEntityId(savedEntity);
                    String finalOperationType = operationTypeHolder[0];
                    String finalBeforeData = beforeDataHolder[0];
                    
                    return createAndSaveAuditLog(
                        entityType, finalEntityId, finalOperationType, 
                        finalBeforeData, afterData, savedEntity
                    ).thenReturn(savedEntity);
                });
            }
            
            return result;
        } catch (Throwable error) {
            logger.error("保存操作审计日志记录失败", error);
            throw error;
        }
    }

    private Object handleDeleteOperation(ProceedingJoinPoint joinPoint, Object[] args,
                                         String entityType, String operationType) throws Throwable {
        try {
            Long entityId = null;
            String beforeData = null;
            
            if (args.length > 0) {
                if (args[0] instanceof Number) {
                    entityId = ((Number) args[0]).longValue();
                    beforeData = fetchEntityBeforeData(entityType, entityId);
                } else if (args[0] instanceof Persistable) {
                    Persistable<?> persistable = (Persistable<?>) args[0];
                    entityId = persistable.getId() != null ? 
                              ((Number) persistable.getId()).longValue() : null;
                    beforeData = serializeEntity(args[0]);
                }
            }
            
            Object result = joinPoint.proceed();
            
            if (result instanceof Mono) {
                Long finalEntityId = entityId;
                String finalBeforeData = beforeData;
                return ((Mono<?>) result).flatMap(deleted -> 
                    createAndSaveAuditLog(
                        entityType, finalEntityId, "DELETE", 
                        finalBeforeData, null, null
                    ).thenReturn(deleted)
                );
            } else if (result instanceof Flux) {
                Long finalEntityId = entityId;
                String finalBeforeData = beforeData;
                return ((Flux<?>) result).flatMap(deleted ->
                    createAndSaveAuditLog(
                        entityType, finalEntityId, "DELETE", 
                        finalBeforeData, null, null
                    ).thenReturn(deleted)
                );
            }
            
            return result;
        } catch (Throwable error) {
            logger.error("删除操作审计日志记录失败", error);
            throw error;
        }
    }

    private Mono<Void> createAndSaveAuditLog(String entityType, Long entityId, 
                                             String operationType, String beforeData, 
                                             String afterData, Object entity) {
        return ReactiveSecurityContextHolder.getContext()
                .map(ctx -> ctx.getAuthentication().getPrincipal())
                .defaultIfEmpty("system")
                .flatMap(principal -> {
                    AuditLog auditLog = new AuditLog();
                    auditLog.setEntityType(entityType);
                    auditLog.setEntityId(entityId);
                    auditLog.setOperationType(operationType);
                    auditLog.setOperator(principal instanceof String ? (String) principal : "system");
                    auditLog.setBeforeData(beforeData);
                    auditLog.setAfterData(afterData);
                    
                    if (beforeData != null && afterData != null) {
                        String[] changedFields = extractChangedFields(beforeData, afterData);
                        auditLog.setChangedFields(changedFields);
                    }
                    
                    auditLog.setDescription(generateDescription(entityType, operationType, entityId));
                    
                    return auditLogRepository.save(auditLog)
                            .doOnSuccess(saved -> logger.debug("审计日志保存成功: {} - {}", 
                                    entityType, operationType))
                            .doOnError(error -> logger.error("审计日志保存失败: {}", 
                                    error.getMessage()))
                            .then();
                })
                .onErrorResume(error -> {
                    logger.error("创建审计日志失败，但不影响主流程: {}", error.getMessage());
                    return Mono.empty();
                });
    }

    private String determineOperationType(String methodName) {
        if (methodName.startsWith("save")) {
            return "SAVE";
        } else if (methodName.startsWith("delete")) {
            return "DELETE";
        }
        return "UNKNOWN";
    }

    private String extractEntityType(String className) {
        if (className.contains("User")) {
            return "User";
        } else if (className.contains("Role")) {
            return "Role";
        } else if (className.contains("Menu")) {
            return "Menu";
        } else if (className.contains("Permission")) {
            return "Permission";
        }
        return className.replace("Repository", "").replace("Impl", "");
    }

    private String fetchEntityBeforeData(String entityType, Long entityId) {
        return null;
    }

    private String serializeEntity(Object entity) {
        try {
            return objectMapper.writeValueAsString(entity);
        } catch (Exception e) {
            logger.error("序列化实体失败: {}", e.getMessage());
            return null;
        }
    }

    private Long extractEntityId(Object entity) {
        if (entity instanceof Persistable) {
            Persistable<?> persistable = (Persistable<?>) entity;
            Object id = persistable.getId();
            return id != null ? ((Number) id).longValue() : null;
        }
        return null;
    }

    private String[] extractChangedFields(String beforeData, String afterData) {
        try {
            JsonNode beforeNode = objectMapper.readTree(beforeData);
            JsonNode afterNode = objectMapper.readTree(afterData);
            
            List<String> changedFields = new ArrayList<>();
            
            beforeNode.fieldNames().forEachRemaining(fieldName -> {
                JsonNode beforeValue = beforeNode.get(fieldName);
                JsonNode afterValue = afterNode.get(fieldName);
                
                if (afterValue == null || !beforeValue.equals(afterValue)) {
                    changedFields.add(fieldName);
                }
            });
            
            afterNode.fieldNames().forEachRemaining(fieldName -> {
                if (!beforeNode.has(fieldName)) {
                    changedFields.add(fieldName);
                }
            });
            
            return changedFields.toArray(new String[0]);
        } catch (Exception e) {
            logger.error("提取变更字段失败: {}", e.getMessage());
            return new String[0];
        }
    }

    private String generateDescription(String entityType, String operationType, Long entityId) {
        String operation = "";
        switch (operationType) {
            case "CREATE":
                operation = "创建";
                break;
            case "UPDATE":
                operation = "更新";
                break;
            case "DELETE":
                operation = "删除";
                break;
            default:
                operation = "操作";
        }
        
        return String.format("%s%s (ID: %s)", operation, entityType, 
                            entityId != null ? entityId : "未知");
    }
}
