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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import reactor.core.publisher.Mono;

public final class AuditLogHelper {

    private static final Logger logger = LoggerFactory.getLogger(AuditLogHelper.class);
    private static final ObjectMapper objectMapper = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .disable(SerializationFeature.FAIL_ON_SELF_REFERENCES);

    private AuditLogHelper() {}

    public static Mono<Void> record(IAuditLogService auditLogService,
                                    String entityType, Long entityId,
                                    String operationType, Object afterEntity) {
        return record(auditLogService, entityType, entityId, operationType, null, afterEntity);
    }

    public static Mono<Void> record(IAuditLogService auditLogService,
                                    String entityType, Long entityId,
                                    String operationType, Object beforeEntity, Object afterEntity) {
        return ReactiveSecurityContextHolder.getContext()
                .map(ctx -> ctx.getAuthentication().getPrincipal())
                .defaultIfEmpty("system")
                .flatMap(principal -> {
                    AuditLog auditLog = new AuditLog();
                    auditLog.generateId();
                    auditLog.setEntityType(entityType);
                    auditLog.setEntityId(entityId != null ? entityId : 0L);
                    auditLog.setOperationType(operationType);
                    auditLog.setOperator(principal instanceof String ? (String) principal : "system");
                    auditLog.setBeforeData(serializeEntity(beforeEntity));
                    auditLog.setAfterData(serializeEntity(afterEntity));
                    auditLog.setDescription(generateDescription(entityType, operationType, entityId));

                    logger.info("记录审计日志: {} {} ID={}, operator={}", operationType, entityType, entityId, auditLog.getOperator());

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

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

    private static 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 : "未知");
    }
}
