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.repository.IAuditLogRepository;
import cn.novalon.gym.manage.sys.audit.service.IAuditLogService;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * 审计日志服务实现类
 * 
 * 文件定义：实现审计日志管理的核心业务逻辑
 * 涉及业务：审计日志的保存、查询、统计、删除等操作
 * 算法：使用R2DBC进行响应式数据库操作，支持分页查询、条件查询、批量操作
 * 
 * @author 张翔
 * @date 2026-04-08
 */
@Service
public class AuditLogService implements IAuditLogService {

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

    private final IAuditLogRepository auditLogRepository;
    private final Executor auditLogExecutor;

    public AuditLogService(IAuditLogRepository auditLogRepository, 
                          Executor auditLogExecutor) {
        this.auditLogRepository = auditLogRepository;
        this.auditLogExecutor = auditLogExecutor;
    }

    @Override
    public Mono<AuditLog> findById(Long id) {
        return auditLogRepository.findById(id);
    }

    @Override
    public Flux<AuditLog> findAll() {
        return auditLogRepository.findAll();
    }

    @Override
    public Flux<AuditLog> findAll(boolean includeDeleted) {
        if (includeDeleted) {
            return auditLogRepository.findAll();
        } else {
            return auditLogRepository.findAll();
        }
    }

    @Override
    public Mono<PageResponse<AuditLog>> findAuditLogsByPage(PageRequest pageRequest) {
        return auditLogRepository.findAll()
                .collectList()
                .map(auditLogs -> {
                    int total = auditLogs.size();
                    int pageSize = pageRequest.getSize();
                    int pageNumber = pageRequest.getPage();
                    int fromIndex = pageNumber * pageSize;
                    int toIndex = Math.min(fromIndex + pageSize, total);
                    
                    List<AuditLog> pageContent = auditLogs.subList(fromIndex, toIndex);
                    int totalPages = (int) Math.ceil((double) total / pageSize);
                    return new PageResponse<>(pageContent, totalPages, total, pageNumber, pageSize);
                });
    }

    @Override
    public Mono<Long> count() {
        return auditLogRepository.findAll()
                .count();
    }

    @Override
    public Flux<AuditLog> findByEntityType(String entityType) {
        return auditLogRepository.findByEntityType(entityType);
    }

    @Override
    public Flux<AuditLog> findByEntityId(Long entityId) {
        return auditLogRepository.findByEntityId(entityId);
    }

    @Override
    public Flux<AuditLog> findByEntityTypeAndEntityId(String entityType, Long entityId) {
        return auditLogRepository.findByEntityTypeAndEntityId(entityType, entityId);
    }

    @Override
    public Flux<AuditLog> findByOperator(String operator) {
        return auditLogRepository.findByOperator(operator);
    }

    @Override
    public Flux<AuditLog> findByOperationType(String operationType) {
        return auditLogRepository.findByOperationType(operationType);
    }

    @Override
    public Flux<AuditLog> findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return auditLogRepository.findByOperationTimeBetween(startTime, endTime);
    }

    @Override
    public Flux<AuditLog> findByEntityTypeAndOperationTimeBetween(String entityType, LocalDateTime startTime, LocalDateTime endTime) {
        return auditLogRepository.findByEntityTypeAndOperationTimeBetween(entityType, startTime, endTime);
    }

    @Override
    public Flux<AuditLog> findByOperatorAndOperationTimeBetween(String operator, LocalDateTime startTime, LocalDateTime endTime) {
        return auditLogRepository.findByOperatorAndOperationTimeBetween(operator, startTime, endTime);
    }

    @Override
    public Mono<Long> countByEntityType(String entityType) {
        return auditLogRepository.countByEntityType(entityType);
    }

    @Override
    public Mono<Long> countByOperationType(String operationType) {
        return auditLogRepository.countByOperationType(operationType);
    }

    @Override
    public Mono<Long> countByOperator(String operator) {
        return auditLogRepository.countByOperator(operator);
    }

    @Override
    public Mono<Long> countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return auditLogRepository.countByOperationTimeBetween(startTime, endTime);
    }

    @Override
    public Mono<AuditLog> save(AuditLog auditLog) {
        return auditLogRepository.save(auditLog);
    }

    @Override
    public Mono<AuditLog> saveAsync(AuditLog auditLog) {
        logger.debug("异步保存审计日志: {} - {}", auditLog.getEntityType(), auditLog.getOperationType());
        
        return auditLogRepository.save(auditLog)
                .doOnSuccess(saved -> logger.debug("审计日志保存成功: ID={}", saved.getId()))
                .doOnError(error -> logger.error("审计日志保存失败: {}", error.getMessage()))
                .subscribeOn(Schedulers.fromExecutor(auditLogExecutor));
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<Void> deleteById(Long id) {
        return auditLogRepository.deleteById(id);
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<Void> logicalDeleteById(Long id) {
        return auditLogRepository.findById(id)
                .flatMap(auditLog -> {
                    auditLog.setDeletedAt(LocalDateTime.now());
                    return auditLogRepository.save(auditLog);
                })
                .then();
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<Void> logicalDeleteByIds(List<Long> ids) {
        return Flux.fromIterable(ids)
                .flatMap(this::logicalDeleteById)
                .then();
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<Void> restoreById(Long id) {
        return auditLogRepository.findById(id)
                .flatMap(auditLog -> {
                    auditLog.setDeletedAt(null);
                    return auditLogRepository.save(auditLog);
                })
                .then();
    }

    @Override
    @Transactional(transactionManager = "connectionFactoryTransactionManager")
    public Mono<Void> restoreByIds(List<Long> ids) {
        return Flux.fromIterable(ids)
                .flatMap(this::restoreById)
                .then();
    }
}
