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

import cn.novalon.manage.sys.audit.domain.AuditLog;
import cn.novalon.manage.sys.audit.domain.AuditLogArchive;
import cn.novalon.manage.sys.audit.repository.IAuditLogArchiveRepository;
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;

/**
 * 审计日志归档服务
 * 
 * 文件定义：封装审计日志归档的业务逻辑
 * 涉及业务：审计日志的归档、查询、清理等操作
 * 算法：定期将历史审计日志移动到归档表
 * 
 * @author 张翔
 * @date 2026-04-01
 */
@Service
public class AuditLogArchiveService {

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

    private final IAuditLogRepository auditLogRepository;
    private final IAuditLogArchiveRepository auditLogArchiveRepository;

    public AuditLogArchiveService(IAuditLogRepository auditLogRepository,
                                   IAuditLogArchiveRepository auditLogArchiveRepository) {
        this.auditLogRepository = auditLogRepository;
        this.auditLogArchiveRepository = auditLogArchiveRepository;
    }

    @Transactional
    public Mono<Long> archiveOldLogs(int daysToKeep) {
        LocalDateTime archiveBefore = LocalDateTime.now().minusDays(daysToKeep);
        
        logger.info("开始归档审计日志，归档时间点: {}", archiveBefore);
        
        return auditLogRepository.findByOperationTimeBetween(
                LocalDateTime.MIN, 
                archiveBefore
        )
        .flatMap(this::archiveLog)
        .count()
        .doOnSuccess(count -> logger.info("审计日志归档完成，共归档 {} 条记录", count))
        .doOnError(error -> logger.error("审计日志归档失败: {}", error.getMessage()));
    }

    private Mono<Void> archiveLog(AuditLog auditLog) {
        AuditLogArchive archive = new AuditLogArchive();
        archive.setEntityType(auditLog.getEntityType());
        archive.setEntityId(auditLog.getEntityId());
        archive.setOperationType(auditLog.getOperationType());
        archive.setOperator(auditLog.getOperator());
        archive.setOperationTime(auditLog.getOperationTime());
        archive.setBeforeData(auditLog.getBeforeData());
        archive.setAfterData(auditLog.getAfterData());
        archive.setChangedFields(auditLog.getChangedFields());
        archive.setIpAddress(auditLog.getIpAddress());
        archive.setUserAgent(auditLog.getUserAgent());
        archive.setDescription(auditLog.getDescription());
        archive.setCreatedAt(auditLog.getCreatedAt());
        archive.setArchivedAt(LocalDateTime.now());
        
        return auditLogArchiveRepository.save(archive)
                .flatMap(savedArchive -> auditLogRepository.deleteById(auditLog.getId()))
                .doOnSuccess(v -> logger.debug("归档审计日志成功: ID={}", auditLog.getId()))
                .doOnError(error -> logger.error("归档审计日志失败: ID={}, 错误: {}", 
                        auditLog.getId(), error.getMessage()))
                .then();
    }

    public Flux<AuditLogArchive> findArchivedLogs(String entityType, LocalDateTime startTime, LocalDateTime endTime) {
        if (entityType != null) {
            return auditLogArchiveRepository.findByEntityType(entityType);
        } else if (startTime != null && endTime != null) {
            return auditLogArchiveRepository.findByArchivedAtBetween(startTime, endTime);
        }
        return Flux.empty();
    }

    public Mono<Long> countArchivedLogs(String entityType) {
        if (entityType != null) {
            return auditLogArchiveRepository.countByEntityType(entityType);
        }
        return auditLogArchiveRepository.count();
    }
}
