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

import cn.novalon.gym.manage.sys.core.domain.Dictionary;
import cn.novalon.gym.manage.sys.core.exception.DictionaryAlreadyExistsException;
import cn.novalon.gym.manage.sys.core.service.IDictionaryService;
import cn.novalon.gym.manage.sys.core.repository.IDictionaryRepository;
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 static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

/**
 * 字典服务单元测试类
 * 
 * @author 张翔
 * @date 2026-03-14
 */
@ExtendWith(MockitoExtension.class)
class DictionaryServiceTest {

    @Mock
    private IDictionaryRepository repository;

    private IDictionaryService service;

    private Dictionary testDictionary;

    @BeforeEach
    void setUp() {
        service = new DictionaryService(repository);

        testDictionary = new Dictionary();
        testDictionary.setId(1L);
        testDictionary.setType("test_type");
        testDictionary.setCode("test_code");
        testDictionary.setName("Test Label");
        testDictionary.setValue("test_value");
        testDictionary.setSort(1);
        testDictionary.setRemark("Test remark");
        testDictionary.setCreatedAt(LocalDateTime.now());
        testDictionary.setUpdatedAt(LocalDateTime.now());
    }

    @Test
    void testFindAll() {
        when(repository.findAll()).thenReturn(Flux.just(testDictionary));

        StepVerifier.create(service.findAll())
                .expectNext(testDictionary)
                .verifyComplete();

        verify(repository).findAll();
    }

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

        StepVerifier.create(service.findById(1L))
                .expectNext(testDictionary)
                .verifyComplete();

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

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

        StepVerifier.create(service.findById(999L))
                .verifyComplete();

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

    @Test
    void testFindByType() {
        when(repository.findByType("test_type")).thenReturn(Flux.just(testDictionary));

        StepVerifier.create(service.findByType("test_type"))
                .expectNext(testDictionary)
                .verifyComplete();

        verify(repository).findByType("test_type");
    }

    @Test
    void testCheckTypeAndCodeExists_True() {
        when(repository.existsByTypeAndCode("test_type", "test_code")).thenReturn(Mono.just(true));

        StepVerifier.create(service.checkTypeAndCodeExists("test_type", "test_code"))
                .expectNext(true)
                .verifyComplete();

        verify(repository).existsByTypeAndCode("test_type", "test_code");
    }

    @Test
    void testCheckTypeAndCodeExists_False() {
        when(repository.existsByTypeAndCode("test_type", "test_code")).thenReturn(Mono.just(false));

        StepVerifier.create(service.checkTypeAndCodeExists("test_type", "test_code"))
                .expectNext(false)
                .verifyComplete();

        verify(repository).existsByTypeAndCode("test_type", "test_code");
    }

    @Test
    void testSave_NewDictionary_Success() {
        Dictionary newDict = new Dictionary();
        newDict.setType("test_type");
        newDict.setCode("test_code");
        newDict.setName("Test Label");
        newDict.setValue("test_value");

        when(repository.existsByTypeAndCode("test_type", "test_code")).thenReturn(Mono.just(false));
        when(repository.save(any())).thenReturn(Mono.just(testDictionary));

        StepVerifier.create(service.save(newDict))
                .expectNextMatches(dict -> dict.getId() != null)
                .verifyComplete();

        verify(repository).existsByTypeAndCode("test_type", "test_code");
        verify(repository).save(any());
    }

    @Test
    void testSave_NewDictionary_AlreadyExists() {
        Dictionary newDict = new Dictionary();
        newDict.setType("test_type");
        newDict.setCode("test_code");

        when(repository.existsByTypeAndCode("test_type", "test_code")).thenReturn(Mono.just(true));

        StepVerifier.create(service.save(newDict))
                .expectError(DictionaryAlreadyExistsException.class)
                .verify();

        verify(repository).existsByTypeAndCode("test_type", "test_code");
        verify(repository, never()).save(any());
    }

    @Test
    void testSave_UpdateExistingDictionary() {
        Dictionary existingDict = new Dictionary();
        existingDict.setId(1L);
        existingDict.setType("test_type");
        existingDict.setCode("test_code");

        when(repository.save(any())).thenReturn(Mono.just(testDictionary));

        StepVerifier.create(service.save(existingDict))
                .expectNextMatches(dict -> dict.getId() == 1L)
                .verifyComplete();

        verify(repository, never()).existsByTypeAndCode(anyString(), anyString());
        verify(repository).save(any());
    }

    @Test
    void testUpdate() {
        Dictionary updateDict = new Dictionary();
        updateDict.setName("Updated Name");
        updateDict.setValue("updated_value");
        updateDict.setRemark("Updated remark");
        updateDict.setSort(2);

        Dictionary existingDict = new Dictionary();
        existingDict.setId(1L);
        existingDict.setType("test_type");
        existingDict.setCode("test_code");
        existingDict.setName("Old Name");
        existingDict.setValue("old_value");
        existingDict.setRemark("Old remark");
        existingDict.setSort(1);

        when(repository.findById(1L)).thenReturn(Mono.just(existingDict));
        when(repository.save(any())).thenReturn(Mono.just(testDictionary));

        StepVerifier.create(service.update(1L, updateDict))
                .expectNextMatches(dict -> dict.getId() == 1L)
                .verifyComplete();

        verify(repository).findById(1L);
        verify(repository).save(any());
    }

    @Test
    void testUpdate_NotFound() {
        Dictionary updateDict = new Dictionary();
        updateDict.setName("Updated Name");

        when(repository.findById(999L)).thenReturn(Mono.empty());

        StepVerifier.create(service.update(999L, updateDict))
                .verifyComplete();

        verify(repository).findById(999L);
        verify(repository, never()).save(any());
    }

    @Test
    void testDeleteById() {
        when(repository.deleteById(1L)).thenReturn(Mono.empty());

        StepVerifier.create(service.deleteById(1L))
                .verifyComplete();

        verify(repository).deleteById(1L);
    }
}