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

import cn.novalon.gym.manage.sys.core.domain.OperationLog;
import cn.novalon.gym.manage.sys.core.query.OperationLogQuery;
import cn.novalon.gym.manage.sys.core.repository.IOperationLogRepository;
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.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.Collections;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class OperationLogServiceTest {

    @Mock
    private IOperationLogRepository logRepository;

    private OperationLogService operationLogService;
    private OperationLog testLog;

    @BeforeEach
    void setUp() {
        operationLogService = new OperationLogService(logRepository);

        testLog = new OperationLog();
        testLog.setId(1L);
        testLog.setUsername("testuser");
        testLog.setOperation("test operation");
        testLog.setMethod("testMethod");
        testLog.setParams("{}");
        testLog.setDuration(100L);
        testLog.setIp("192.168.1.1");
        testLog.setStatus("1");
        testLog.setCreatedAt(LocalDateTime.now());
    }

    @Test
    void testSave() {
        when(logRepository.save(any(OperationLog.class))).thenReturn(Mono.just(testLog));

        Mono<OperationLog> result = operationLogService.save(testLog);

        StepVerifier.create(result)
                .expectNextMatches(log -> log.getId().equals(1L) &&
                        log.getUsername().equals("testuser") &&
                        log.getCreatedAt() != null)
                .verifyComplete();

        verify(logRepository).save(any(OperationLog.class));
    }

    @Test
    void testFindAll() {
        when(logRepository.findAll()).thenReturn(Flux.just(testLog));

        Flux<OperationLog> result = operationLogService.findAll();

        StepVerifier.create(result)
                .expectNext(testLog)
                .verifyComplete();

        verify(logRepository).findAll();
    }

    @Test
    void testFindByUsername() {
        when(logRepository.findByUsername("testuser")).thenReturn(Flux.just(testLog));

        Flux<OperationLog> result = operationLogService.findByUsername("testuser");

        StepVerifier.create(result)
                .expectNext(testLog)
                .verifyComplete();

        verify(logRepository).findByUsername("testuser");
    }

    @Test
    void testCount() {
        when(logRepository.count()).thenReturn(Mono.just(100L));

        Mono<Long> result = operationLogService.count();

        StepVerifier.create(result)
                .expectNext(100L)
                .verifyComplete();

        verify(logRepository).count();
    }

    @Test
    void testCountToday() {
        when(logRepository.countByCreatedAtAfter(any(LocalDateTime.class))).thenReturn(Mono.just(10L));

        Mono<Long> result = operationLogService.countToday();

        StepVerifier.create(result)
                .expectNext(10L)
                .verifyComplete();

        verify(logRepository).countByCreatedAtAfter(any(LocalDateTime.class));
    }

    @Test
    void testFindById() {
        when(logRepository.findById(1L)).thenReturn(Mono.just(testLog));

        Mono<OperationLog> result = operationLogService.findById(1L);

        StepVerifier.create(result)
                .expectNext(testLog)
                .verifyComplete();

        verify(logRepository).findById(1L);
    }

    @Test
    void testFindById_NotFound() {
        when(logRepository.findById(999L)).thenReturn(Mono.empty());

        Mono<OperationLog> result = operationLogService.findById(999L);

        StepVerifier.create(result)
                .verifyComplete();

        verify(logRepository).findById(999L);
    }

    @Test
    void testFindByQueryWithPagination() {
        PageResponse<OperationLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(Collections.singletonList(testLog));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        pageResponse.setCurrentPage(0);
        pageResponse.setPageSize(10);

        PageRequest pageRequest = new PageRequest();
        pageRequest.setPage(0);
        pageRequest.setSize(10);

        OperationLogQuery query = new OperationLogQuery();

        when(logRepository.findByQueryWithPagination(any(OperationLogQuery.class), any(PageRequest.class)))
                .thenReturn(Mono.just(pageResponse));

        Mono<PageResponse<OperationLog>> result = operationLogService.findByQueryWithPagination(query, pageRequest);

        StepVerifier.create(result)
                .expectNextMatches(response -> response.getContent().size() == 1 &&
                        response.getTotalElements() == 1L &&
                        response.getTotalPages() == 1)
                .verifyComplete();

        verify(logRepository).findByQueryWithPagination(any(OperationLogQuery.class), any(PageRequest.class));
    }
}