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

import cn.novalon.gym.manage.sys.audit.domain.AuditLog;
import cn.novalon.gym.manage.sys.audit.domain.AuditLogArchive;
import cn.novalon.gym.manage.sys.audit.repository.IAuditLogArchiveRepository;
import cn.novalon.gym.manage.sys.audit.repository.IAuditLogRepository;
import cn.novalon.gym.manage.sys.audit.service.IAuditLogArchiveService;
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-14
 */
@Service
public class AuditLogArchiveService implements IAuditLogArchiveService {

    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;
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    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()));
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<AuditLogArchive> archiveLog(AuditLog auditLog) {
        AuditLogArchive archive = convertToArchive(auditLog);
        
        return auditLogArchiveRepository.save(archive)
                .doOnSuccess(saved -> {
                    logger.debug("审计日志归档成功: ID={}, 操作类型={}", 
                            saved.getId(), saved.getOperationType());
                    
                    auditLogRepository.deleteById(auditLog.getId())
                            .doOnSuccess(v -> logger.debug("原始日志删除成功: ID={}", auditLog.getId()))
                            .doOnError(error -> logger.error("原始日志删除失败: ID={}, {}", 
                                    auditLog.getId(), error.getMessage()))
                            .subscribe();
                })
                .doOnError(error -> logger.error("审计日志归档失败: ID={}, {}", 
                        auditLog.getId(), error.getMessage()));
    }

    @Override
    public Flux<AuditLogArchive> findArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
        return auditLogArchiveRepository.findByOperationTimeBetween(startDate, endDate);
    }

    @Override
    public Flux<AuditLogArchive> findArchivedLogsByEntityType(String entityType) {
        return auditLogArchiveRepository.findByEntityType(entityType);
    }

    @Override
    public Mono<AuditLogArchive> findArchivedLogById(Long id) {
        return auditLogArchiveRepository.findById(id);
    }

    @Override
    public Mono<Long> countArchivedLogs() {
        return auditLogArchiveRepository.count();
    }

    @Override
    public Mono<Long> countArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
        return auditLogArchiveRepository.findByOperationTimeBetween(startDate, endDate)
                .count();
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<Void> deleteArchivedLogsOlderThan(LocalDateTime date) {
        return auditLogArchiveRepository.findByOperationTimeBetween(LocalDateTime.MIN, date)
                .flatMap(archive -> auditLogArchiveRepository.deleteById(archive.getId()))
                .then()
                .doOnSuccess(v -> logger.info("删除早于 {} 的归档日志完成", date))
                .doOnError(error -> logger.error("删除归档日志失败: {}", error.getMessage()));
    }

    @Override
    public Mono<Long> getArchiveStatistics() {
        return auditLogArchiveRepository.count()
                .doOnNext(count -> logger.info("归档日志统计: {} 条记录", count));
    }

    @Override
    public Mono<Boolean> isLogArchived(Long auditLogId) {
        return auditLogArchiveRepository.findAll()
                .filter(archive -> archive.getEntityId() != null && archive.getEntityId().equals(auditLogId))
                .hasElements()
                .doOnNext(archived -> logger.debug("日志 ID={} 是否已归档: {}", auditLogId, archived));
    }

    private AuditLogArchive convertToArchive(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.setIpAddress(auditLog.getIpAddress());
        archive.setUserAgent(auditLog.getUserAgent());
        archive.setArchivedAt(LocalDateTime.now());
        
        if (auditLog.getDescription() != null) {
            archive.setDescription(auditLog.getDescription());
        }
        
        return archive;
    }
}