package cn.novalon.gym.manage.db.repository;

import cn.novalon.gym.manage.sys.audit.domain.AuditLog;
import cn.novalon.gym.manage.sys.audit.repository.IAuditLogRepository;
import cn.novalon.gym.manage.db.converter.AuditLogConverter;
import cn.novalon.gym.manage.db.dao.AuditLogDao;
import cn.novalon.gym.manage.db.entity.AuditLogEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;

/**
 * 审计日志仓储实现类
 * 
 * @author 张翔
 * @date 2026-04-08
 */
@Repository
public class AuditLogRepository implements IAuditLogRepository {

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

    private final AuditLogDao auditLogDao;
    private final AuditLogConverter auditLogConverter;
    private final R2dbcEntityTemplate r2dbcEntityTemplate;

    public AuditLogRepository(AuditLogDao auditLogDao, AuditLogConverter auditLogConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.auditLogDao = auditLogDao;
        this.auditLogConverter = auditLogConverter;
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    @Override
    public Mono<AuditLog> findById(Long id) {
        return auditLogDao.findById(id)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Mono<AuditLog> save(AuditLog auditLog) {
        AuditLogEntity entity = auditLogConverter.toEntity(auditLog);
        if (entity.isNew()) {
            return r2dbcEntityTemplate.insert(AuditLogEntity.class)
                    .using(entity)
                    .doOnNext(e -> e.markNotNew())
                    .map(auditLogConverter::toDomain);
        }
        return auditLogDao.save(entity)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Mono<Void> deleteById(Long id) {
        return auditLogDao.deleteById(id);
    }

    @Override
    public Flux<AuditLog> findAll() {
        return auditLogDao.findByDeletedAtIsNull()
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByEntityType(String entityType) {
        return auditLogDao.findByEntityTypeAndDeletedAtIsNull(entityType)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByEntityId(Long entityId) {
        return auditLogDao.findByEntityIdAndDeletedAtIsNull(entityId)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByEntityTypeAndEntityId(String entityType, Long entityId) {
        return auditLogDao.findByEntityTypeAndEntityIdAndDeletedAtIsNull(entityType, entityId)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByOperator(String operator) {
        return auditLogDao.findByOperatorAndDeletedAtIsNull(operator)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByOperationType(String operationType) {
        return auditLogDao.findByOperationTypeAndDeletedAtIsNull(operationType)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return auditLogDao.findByOperationTimeBetweenAndDeletedAtIsNull(startTime, endTime)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByEntityTypeAndOperationTimeBetween(
        String entityType, 
        LocalDateTime startTime, 
        LocalDateTime endTime
    ) {
        return auditLogDao.findByEntityTypeAndOperationTimeBetweenAndDeletedAtIsNull(entityType, startTime, endTime)
                .map(auditLogConverter::toDomain);
    }

    @Override
    public Flux<AuditLog> findByOperatorAndOperationTimeBetween(
        String operator, 
        LocalDateTime startTime, 
        LocalDateTime endTime
    ) {
        return auditLogDao.findByOperatorAndOperationTimeBetweenAndDeletedAtIsNull(operator, startTime, endTime)
                .map(auditLogConverter::toDomain);
    }

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

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

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

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