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.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

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

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * AuditLogService 单元测试
 * 
 * @author 张翔
 * @date 2026-04-14
 */
@ExtendWith(MockitoExtension.class)
class AuditLogServiceTest {

    @Mock
    private IAuditLogRepository auditLogRepository;

    @Mock
    private Executor auditLogExecutor;

    private AuditLogService auditLogService;

    @BeforeEach
    void setUp() {
        auditLogService = new AuditLogService(auditLogRepository, auditLogExecutor);
        
        lenient().doAnswer(invocation -> {
            Runnable task = invocation.getArgument(0);
            task.run();
            return null;
        }).when(auditLogExecutor).execute(any(Runnable.class));
    }

    @Test
    @DisplayName("根据ID查询审计日志 - 成功")
    void findById_whenExists_shouldReturnAuditLog() {
        AuditLog auditLog = createTestAuditLog(1L);
        when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog));

        StepVerifier.create(auditLogService.findById(1L))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("根据ID查询审计日志 - 不存在")
    void findById_whenNotExists_shouldReturnEmpty() {
        when(auditLogRepository.findById(999L)).thenReturn(Mono.empty());

        StepVerifier.create(auditLogService.findById(999L))
                .verifyComplete();
    }

    @Test
    @DisplayName("查询所有审计日志")
    void findAll_shouldReturnAllAuditLogs() {
        AuditLog auditLog1 = createTestAuditLog(1L);
        AuditLog auditLog2 = createTestAuditLog(2L);
        when(auditLogRepository.findAll()).thenReturn(Flux.just(auditLog1, auditLog2));

        StepVerifier.create(auditLogService.findAll())
                .expectNext(auditLog1)
                .expectNext(auditLog2)
                .verifyComplete();
    }

    @Test
    @DisplayName("分页查询审计日志")
    void findAuditLogsByPage_shouldReturnPageResponse() {
        AuditLog auditLog1 = createTestAuditLog(1L);
        AuditLog auditLog2 = createTestAuditLog(2L);
        AuditLog auditLog3 = createTestAuditLog(3L);
        
        when(auditLogRepository.findAll()).thenReturn(Flux.just(auditLog1, auditLog2, auditLog3));

        PageRequest pageRequest = new PageRequest();
        pageRequest.setPage(0);
        pageRequest.setSize(2);
        
        StepVerifier.create(auditLogService.findAuditLogsByPage(pageRequest))
                .expectNextMatches(pageResponse -> 
                        pageResponse.getContent().size() == 2 &&
                        pageResponse.getTotalPages() == 2 &&
                        pageResponse.getTotalElements() == 3)
                .verifyComplete();
    }

    @Test
    @DisplayName("统计审计日志总数")
    void count_shouldReturnTotalCount() {
        when(auditLogRepository.findAll()).thenReturn(Flux.just(
                createTestAuditLog(1L),
                createTestAuditLog(2L),
                createTestAuditLog(3L)
        ));

        StepVerifier.create(auditLogService.count())
                .expectNext(3L)
                .verifyComplete();
    }

    @Test
    @DisplayName("按实体类型查询审计日志")
    void findByEntityType_shouldReturnAuditLogs() {
        AuditLog auditLog = createTestAuditLog(1L);
        when(auditLogRepository.findByEntityType("User")).thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByEntityType("User"))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按实体ID查询审计日志")
    void findByEntityId_shouldReturnAuditLogs() {
        AuditLog auditLog = createTestAuditLog(1L);
        when(auditLogRepository.findByEntityId(100L)).thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByEntityId(100L))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按实体类型和实体ID查询审计日志")
    void findByEntityTypeAndEntityId_shouldReturnAuditLogs() {
        AuditLog auditLog = createTestAuditLog(1L);
        when(auditLogRepository.findByEntityTypeAndEntityId("User", 100L))
                .thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByEntityTypeAndEntityId("User", 100L))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按操作人查询审计日志")
    void findByOperator_shouldReturnAuditLogs() {
        AuditLog auditLog = createTestAuditLog(1L);
        when(auditLogRepository.findByOperator("admin")).thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByOperator("admin"))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按操作类型查询审计日志")
    void findByOperationType_shouldReturnAuditLogs() {
        AuditLog auditLog = createTestAuditLog(1L);
        when(auditLogRepository.findByOperationType("CREATE")).thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByOperationType("CREATE"))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按时间范围查询审计日志")
    void findByOperationTimeBetween_shouldReturnAuditLogs() {
        LocalDateTime startTime = LocalDateTime.now().minusDays(1);
        LocalDateTime endTime = LocalDateTime.now();
        AuditLog auditLog = createTestAuditLog(1L);
        
        when(auditLogRepository.findByOperationTimeBetween(startTime, endTime))
                .thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByOperationTimeBetween(startTime, endTime))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按实体类型和时间范围查询审计日志")
    void findByEntityTypeAndOperationTimeBetween_shouldReturnAuditLogs() {
        LocalDateTime startTime = LocalDateTime.now().minusDays(1);
        LocalDateTime endTime = LocalDateTime.now();
        AuditLog auditLog = createTestAuditLog(1L);
        
        when(auditLogRepository.findByEntityTypeAndOperationTimeBetween("User", startTime, endTime))
                .thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByEntityTypeAndOperationTimeBetween("User", startTime, endTime))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按操作人和时间范围查询审计日志")
    void findByOperatorAndOperationTimeBetween_shouldReturnAuditLogs() {
        LocalDateTime startTime = LocalDateTime.now().minusDays(1);
        LocalDateTime endTime = LocalDateTime.now();
        AuditLog auditLog = createTestAuditLog(1L);
        
        when(auditLogRepository.findByOperatorAndOperationTimeBetween("admin", startTime, endTime))
                .thenReturn(Flux.just(auditLog));

        StepVerifier.create(auditLogService.findByOperatorAndOperationTimeBetween("admin", startTime, endTime))
                .expectNext(auditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("按实体类型统计数量")
    void countByEntityType_shouldReturnCount() {
        when(auditLogRepository.countByEntityType("User")).thenReturn(Mono.just(5L));

        StepVerifier.create(auditLogService.countByEntityType("User"))
                .expectNext(5L)
                .verifyComplete();
    }

    @Test
    @DisplayName("按操作类型统计数量")
    void countByOperationType_shouldReturnCount() {
        when(auditLogRepository.countByOperationType("CREATE")).thenReturn(Mono.just(3L));

        StepVerifier.create(auditLogService.countByOperationType("CREATE"))
                .expectNext(3L)
                .verifyComplete();
    }

    @Test
    @DisplayName("按操作人统计数量")
    void countByOperator_shouldReturnCount() {
        when(auditLogRepository.countByOperator("admin")).thenReturn(Mono.just(2L));

        StepVerifier.create(auditLogService.countByOperator("admin"))
                .expectNext(2L)
                .verifyComplete();
    }

    @Test
    @DisplayName("按时间范围统计数量")
    void countByOperationTimeBetween_shouldReturnCount() {
        LocalDateTime startTime = LocalDateTime.now().minusDays(1);
        LocalDateTime endTime = LocalDateTime.now();
        
        when(auditLogRepository.countByOperationTimeBetween(startTime, endTime))
                .thenReturn(Mono.just(10L));

        StepVerifier.create(auditLogService.countByOperationTimeBetween(startTime, endTime))
                .expectNext(10L)
                .verifyComplete();
    }

    @Test
    @DisplayName("保存审计日志")
    void save_shouldReturnSavedAuditLog() {
        AuditLog auditLog = createTestAuditLog(null);
        AuditLog savedAuditLog = createTestAuditLog(1L);
        
        when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(savedAuditLog));

        StepVerifier.create(auditLogService.save(auditLog))
                .expectNext(savedAuditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("异步保存审计日志")
    void saveAsync_shouldReturnSavedAuditLog() {
        AuditLog auditLog = createTestAuditLog(null);
        AuditLog savedAuditLog = createTestAuditLog(1L);
        
        when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(savedAuditLog));

        StepVerifier.create(auditLogService.saveAsync(auditLog))
                .expectNext(savedAuditLog)
                .verifyComplete();
    }

    @Test
    @DisplayName("根据ID删除审计日志")
    void deleteById_shouldDeleteAuditLog() {
        when(auditLogRepository.deleteById(1L)).thenReturn(Mono.empty());

        StepVerifier.create(auditLogService.deleteById(1L))
                .verifyComplete();
    }

    @Test
    @DisplayName("逻辑删除审计日志")
    void logicalDeleteById_shouldSetDeletedAt() {
        AuditLog auditLog = createTestAuditLog(1L);
        AuditLog deletedAuditLog = createTestAuditLog(1L);
        deletedAuditLog.setDeletedAt(LocalDateTime.now());
        
        when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog));
        when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(deletedAuditLog));

        StepVerifier.create(auditLogService.logicalDeleteById(1L))
                .verifyComplete();
    }

    @Test
    @DisplayName("批量逻辑删除审计日志")
    void logicalDeleteByIds_shouldDeleteMultipleAuditLogs() {
        AuditLog auditLog1 = createTestAuditLog(1L);
        AuditLog auditLog2 = createTestAuditLog(2L);
        
        when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog1));
        when(auditLogRepository.findById(2L)).thenReturn(Mono.just(auditLog2));
        when(auditLogRepository.save(any(AuditLog.class))).thenReturn(Mono.just(auditLog1));

        StepVerifier.create(auditLogService.logicalDeleteByIds(List.of(1L, 2L)))
                .verifyComplete();
    }

    @Test
    @DisplayName("恢复逻辑删除的审计日志")
    void restoreById_shouldClearDeletedAt() {
        AuditLog auditLog = createTestAuditLog(1L);
        auditLog.setDeletedAt(LocalDateTime.now());
        AuditLog restoredAuditLog = createTestAuditLog(1L);
        restoredAuditLog.setDeletedAt(null);
        
        when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog));
        when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(restoredAuditLog));

        StepVerifier.create(auditLogService.restoreById(1L))
                .verifyComplete();
    }

    private AuditLog createTestAuditLog(Long id) {
        AuditLog auditLog = new AuditLog();
        auditLog.setId(id);
        auditLog.setEntityType("User");
        auditLog.setEntityId(100L);
        auditLog.setOperator("admin");
        auditLog.setOperationType("CREATE");
        auditLog.setOperationTime(LocalDateTime.now());
        auditLog.setDescription("创建用户");
        auditLog.setIpAddress("192.168.1.1");
        auditLog.setUserAgent("Mozilla/5.0");
        return auditLog;
    }
}