package com.ecep.contract.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;

import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.model.Voable;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 基础Service测试类，提供通用的测试方法，用于验证实体到VO的转换和缓存逻辑
 */
public abstract class BaseServiceTest<T extends Voable<VO>, VO> {

    @Mock
    protected RedisTemplate<String, Object> redisTemplate;

    protected abstract IEntityService<T> getEntityService();

    protected abstract QueryService<VO> getQueryService();

    protected abstract VoableService<T, VO> getVoableService();

    protected abstract T createMockEntity();

    protected abstract VO createMockVo();

    protected abstract String getRedisKeyPrefix();

    protected abstract Integer getMockId();

    protected abstract String getSearchText();

    private ObjectMapper objectMapper;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
        objectMapper = new ObjectMapper();
    }

    /**
     * 测试findById方法，验证实体-VO转换功能
     */
    @Test
    public void testFindById_VoConversion() {
        Integer id = getMockId();
        T mockEntity = createMockEntity();
        VO expectedVo = createMockVo();

        // 模拟Redis缓存未命中和服务方法调用
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
        when(getEntityService().getById(id)).thenReturn(mockEntity);
        when(getQueryService().findById(id)).thenReturn(expectedVo);

        // 执行方法
        VO result = getQueryService().findById(id);

        // 验证结果
        assertNotNull(result);
        assertEquals(expectedVo, result);

        // 验证方法调用
        verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
        verify(getEntityService(), times(1)).getById(id);
        verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result), anyLong(),
                any(TimeUnit.class));
    }

    /**
     * 测试findById方法，验证Redis缓存命中情况
     */
    @Test
    public void testFindById_CacheHit() {
        Integer id = getMockId();
        VO expectedVo = createMockVo();

        // 模拟Redis缓存命中
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(expectedVo);

        // 执行方法
        VO result = getQueryService().findById(id);

        // 验证结果
        assertNotNull(result);
        assertEquals(expectedVo, result);

        // 验证方法调用 - 不应调用getById
        verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
        verify(getEntityService(), times(0)).getById(anyInt());
    }

    /**
     * 测试findById方法，验证实体不存在情况
     */
    @Test
    public void testFindById_EntityNotFound() {
        Integer id = getMockId();

        // 模拟Redis缓存未命中和实体不存在
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
        when(getEntityService().getById(id)).thenReturn(null);
        when(getQueryService().findById(id)).thenReturn(null);

        // 执行方法
        VO result = getQueryService().findById(id);

        // 验证结果
        assertNull(result);

        // 验证方法调用
        verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
        verify(getEntityService(), times(1)).getById(id);
    }

    /**
     * 测试updateByVo方法，验证实体更新和缓存刷新功能
     */
    @Test
    public void testUpdateByVo() {
        Integer id = getMockId();
        T mockEntity = createMockEntity();
        VO mockVo = createMockVo();

        // 模拟getById方法调用
        when(getEntityService().getById(id)).thenReturn(mockEntity);
        when(getEntityService().save(mockEntity)).thenReturn(mockEntity);

        // 执行方法
        getVoableService().updateByVo(mockEntity, mockVo);

        // 验证结果 - 直接检查传入的实体对象是否被更新
        assertNotNull(mockEntity);

        // 验证方法调用
        verify(getEntityService(), times(1)).getById(id);
        verify(getEntityService(), times(1)).save(mockEntity);
        verify(redisTemplate, times(1)).delete(getRedisKeyPrefix() + id);
    }

    /**
     * 测试findAll方法，验证分页查询和VO转换功能
     */
    @Test
    public void testFindAll_Pagination() {
        List<T> entityList = new ArrayList<>();
        entityList.add(createMockEntity());
        Page<T> page = new PageImpl<>(entityList);
        // 使用默认的分页参数
        Pageable pageable = PageRequest.of(0, 10);
        Specification<T> spec = getEntityService().getSpecification(getSearchText());

        // 模拟findAll方法调用
        when(getEntityService().findAll(spec, pageable)).thenReturn(page);

        // 执行方法（这里需要具体的QueryService实现来验证）
        // 子类需要根据实际实现补充测试

        // 验证方法调用
        verify(getEntityService(), times(1)).findAll(spec, pageable);
    }

    /**
     * 测试save方法后缓存清理功能
     */
    @Test
    public void testSave_CacheEviction() {
        T mockEntity = createMockEntity();

        // 模拟save方法调用
        when(getEntityService().save(mockEntity)).thenReturn(mockEntity);

        // 执行方法
        T result = getEntityService().save(mockEntity);

        // 验证结果
        assertNotNull(result);
        assertEquals(mockEntity, result);

        // 验证方法调用
        verify(getEntityService(), times(1)).save(mockEntity);
        // 验证缓存清理（如果Service中有实现缓存清理逻辑）
        // 子类需要根据实际实现补充验证
    }

    /**
     * 测试delete方法后缓存清理功能
     */
    @Test
    public void testDelete_CacheEviction() {
        T mockEntity = createMockEntity();

        // 模拟delete方法调用
        doNothing().when(getEntityService()).delete(mockEntity);

        // 执行方法
        getEntityService().delete(mockEntity);

        // 验证方法调用
        verify(getEntityService(), times(1)).delete(mockEntity);
        // 验证缓存清理（如果Service中有实现缓存清理逻辑）
        // 子类需要根据实际实现补充验证
    }

    /**
     * 测试缓存过期设置
     */
    @Test
    public void testCacheExpiration() {
        Integer id = getMockId();
        T mockEntity = createMockEntity();
        VO expectedVo = createMockVo();

        // 模拟Redis缓存未命中和服务方法调用
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
        when(getEntityService().getById(id)).thenReturn(mockEntity);
        when(getQueryService().findById(id)).thenReturn(expectedVo);

        // 执行方法
        VO result = getQueryService().findById(id);

        // 验证缓存设置了过期时间
        verify(redisTemplate, times(1)).opsForValue().set(
                eq(getRedisKeyPrefix() + id),
                eq(result),
                anyLong(),
                eq(TimeUnit.MINUTES));
    }
}