package cn.novalon.gym.manage.sys.audit.controller;

import cn.novalon.gym.manage.sys.audit.domain.AuditLog;
import cn.novalon.gym.manage.sys.audit.dto.AuditLogQueryRequest;
import cn.novalon.gym.manage.sys.audit.dto.AuditLogStatistics;
import cn.novalon.gym.manage.sys.audit.service.IAuditLogService;
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 org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.*;

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

    @Mock
    private IAuditLogService auditLogService;

    private WebTestClient webTestClient;
    private AuditLogController auditLogController;

    @BeforeEach
    void setUp() {
        auditLogController = new AuditLogController(auditLogService);
        webTestClient = WebTestClient.bindToController(auditLogController).build();
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/1")
                .exchange()
                .expectStatus().isOk()
                .expectBody(AuditLog.class)
                .isEqualTo(auditLog);
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/999")
                .exchange()
                .expectStatus().isOk()
                .expectBody().isEmpty();
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/entity-type/User")
                .exchange()
                .expectStatus().isOk()
                .expectBodyList(AuditLog.class)
                .hasSize(2)
                .contains(auditLog1, auditLog2);
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/entity/100")
                .exchange()
                .expectStatus().isOk()
                .expectBodyList(AuditLog.class)
                .hasSize(1)
                .contains(auditLog);
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/operator/admin")
                .exchange()
                .expectStatus().isOk()
                .expectBodyList(AuditLog.class)
                .hasSize(1)
                .contains(auditLog);
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/operation-type/CREATE")
                .exchange()
                .expectStatus().isOk()
                .expectBodyList(AuditLog.class)
                .hasSize(1)
                .contains(auditLog);
    }

    @Test
    @DisplayName("按时间范围查询审计日志")
    void findByTimeRange_shouldReturnAuditLogs() {
        LocalDateTime startTime = LocalDateTime.now().minusDays(1);
        LocalDateTime endTime = LocalDateTime.now();
        AuditLog auditLog = createTestAuditLog(1L);

        when(auditLogService.findByOperationTimeBetween(startTime, endTime))
                .thenReturn(Flux.just(auditLog));

        webTestClient.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/api/audit-logs/time-range")
                        .queryParam("startTime", startTime)
                        .queryParam("endTime", endTime)
                        .build())
                .exchange()
                .expectStatus().isOk()
                .expectBodyList(AuditLog.class)
                .hasSize(1)
                .contains(auditLog);
    }

    @Test
    @DisplayName("获取审计日志统计信息")
    void getStatistics_shouldReturnStatistics() {
        webTestClient.get()
                .uri("/api/audit-logs/statistics")
                .exchange()
                .expectStatus().isOk()
                .expectBody(AuditLogStatistics.class)
                .value(returnedStatistics -> {
                    assertNotNull(returnedStatistics);
                    assertNull(returnedStatistics.getTotalCount());
                });
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/count/entity-type/User")
                .exchange()
                .expectStatus().isOk()
                .expectBody(Long.class)
                .isEqualTo(10L);
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/count/operator/admin")
                .exchange()
                .expectStatus().isOk()
                .expectBody(Long.class)
                .isEqualTo(5L);
    }

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

        webTestClient.get()
                .uri("/api/audit-logs/count/operation-type/CREATE")
                .exchange()
                .expectStatus().isOk()
                .expectBody(Long.class)
                .isEqualTo(3L);
    }

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