package cn.novalon.gym.manage.sys.audit;

import cn.novalon.gym.manage.sys.audit.domain.AuditLog;
import cn.novalon.gym.manage.sys.audit.service.IAuditLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Aspect
@Component
@Deprecated
public class AuditLogAspect {

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

    private final IAuditLogService auditLogService;

    public AuditLogAspect(IAuditLogService auditLogService) {
        this.auditLogService = auditLogService;
        logger.info("=== AuditLogAspect 初始化完成 ===");
    }

    @Before("execution(* cn.novalon.gym.manage.sys.core.service.impl.SysUserService.createUser(..))")
    public void testAopWorking() {
        logger.info("=== AuditLogAspect @Before 测试: SysUserService.createUser 被调用 ===");
    }

    @Around("@annotation(auditable)")
    public Object logAuditEvent(ProceedingJoinPoint joinPoint, Auditable auditable) throws Throwable {
        String methodName = ((MethodSignature) joinPoint.getSignature()).getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String entityType = auditable.entityType();
        String operationType = auditable.operationType();

        logger.debug("审计切面拦截: {}.{}(), entityType={}, operationType={}", className, methodName, entityType, operationType);

        try {
            Object result = joinPoint.proceed();

            if (result instanceof Mono) {
                return ((Mono<Object>) result).flatMap(retValue -> {
                    Long entityId = extractIdFromResult(retValue);
                    String afterData = serializeEntity(retValue);
                    return createAndSaveAuditLog(
                            entityType, entityId, operationType,
                            null, afterData
                    ).thenReturn(retValue);
                });
            } else if (result instanceof Flux) {
                return ((Flux<Object>) result).collectList()
                        .flatMapMany(list -> {
                            String afterData = serializeEntity(list);
                            return createAndSaveAuditLog(
                                    entityType, null, operationType,
                                    null, afterData
                            ).thenMany(Flux.fromIterable(list));
                        });
            }

            return result;
        } catch (Throwable error) {
            logger.error("审计日志记录失败: {}.{}()", className, methodName, error);
            throw error;
        }
    }

    private Mono<Void> createAndSaveAuditLog(String entityType, Long entityId,
                                             String operationType, String beforeData,
                                             String afterData) {
        logger.debug("创建审计日志: entityType={}, entityId={}, operationType={}", entityType, entityId, operationType);

        return ReactiveSecurityContextHolder.getContext()
                .map(ctx -> ctx.getAuthentication().getPrincipal())
                .defaultIfEmpty("system")
                .flatMap(principal -> {
                    AuditLog auditLog = new AuditLog();
                    auditLog.setEntityType(entityType);
                    auditLog.setEntityId(entityId != null ? entityId : 0L);
                    auditLog.setOperationType(operationType);
                    auditLog.setOperator(principal instanceof String ? (String) principal : "system");
                    auditLog.setBeforeData(beforeData);
                    auditLog.setAfterData(afterData);
                    auditLog.setDescription(generateDescription(entityType, operationType, entityId));

                    return auditLogService.saveAsync(auditLog)
                            .doOnSuccess(saved -> logger.debug("审计日志保存成功: {} - {}, ID={}",
                                    entityType, operationType, saved.getId()))
                            .doOnError(error -> logger.error("审计日志保存失败: {}", error.getMessage()))
                            .then();
                })
                .onErrorResume(error -> {
                    logger.error("创建审计日志失败，但不影响主流程: {}", error.getMessage(), error);
                    return Mono.empty();
                });
    }

    private Long extractIdFromResult(Object result) {
        if (result == null) {
            return null;
        }
        try {
            var getIdMethod = result.getClass().getMethod("getId");
            Object id = getIdMethod.invoke(result);
            if (id instanceof Number) {
                return ((Number) id).longValue();
            }
            if (id instanceof String) {
                try {
                    return Long.parseLong((String) id);
                } catch (NumberFormatException e) {
                    return null;
                }
            }
        } catch (NoSuchMethodException e) {
            logger.debug("结果对象没有getId方法: {}", result.getClass().getSimpleName());
        } catch (Exception e) {
            logger.debug("提取结果ID失败: {}", e.getMessage());
        }
        return null;
    }

    private String serializeEntity(Object entity) {
        try {
            ObjectMapper mapper = new ObjectMapper()
                    .registerModule(new JavaTimeModule())
                    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                    .disable(SerializationFeature.FAIL_ON_SELF_REFERENCES);
            return mapper.writeValueAsString(entity);
        } catch (Exception e) {
            logger.error("序列化实体失败: {}", e.getMessage());
            return null;
        }
    }

    private String generateDescription(String entityType, String operationType, Long entityId) {
        String operation = switch (operationType) {
            case "CREATE" -> "创建";
            case "UPDATE" -> "更新";
            case "DELETE" -> "删除";
            default -> "操作";
        };

        return String.format("%s%s (ID: %s)", operation, entityType,
                entityId != null ? entityId : "未知");
    }
}
