package com.ecep.contract.ds.contract.service;

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

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.redis.core.RedisTemplate;

import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.ds.contract.repository.ContractRepository;
import com.ecep.contract.ds.contract.model.Contract;
import com.ecep.contract.model.ContractType;
import com.ecep.contract.ds.project.model.Project;
import com.ecep.contract.vo.ContractVo;
import com.ecep.contract.service.BaseServiceTest;
import com.ecep.contract.service.VoableService;

/**
 * ContractService的集成测试类，用于验证ContractService的实体-VO转换和缓存逻辑
 */
@SpringBootTest
class ContractServiceIntegrationTest extends BaseServiceTest<Contract, ContractVo> {

    @MockBean
    private ContractRepository contractRepository;

    @MockBean
    private RedisTemplate<String, Object> redisTemplate;

    @InjectMocks
    private ContractService contractService;

    @Override
    protected IEntityService<Contract> getEntityService() {
        return contractService;
    }

    @Override
    protected QueryService<ContractVo> getQueryService() {
        return contractService;
    }

    @Override
    protected VoableService<Contract, ContractVo> getVoableService() {
        return contractService;
    }

    @Override
    protected Contract createMockEntity() {
        Contract contract = new Contract();
        contract.setId(getMockId());
        contract.setName("测试合同");
        contract.setCode("CONTRACT-001");
        
        // 创建并设置项目对象
        Project project = new Project();
        project.setId(1);
        contract.setProject(project);
        
        // 创建并设置合同类型对象
        ContractType contractType = new ContractType();
        contractType.setId(1);
        contract.setType(contractType);
        
        contract.setAmount(10000.0);
        contract.setState("B"); // 根据Contract类中的注释，B是最常见的状态
        return contract;
    }

    @Override
    protected ContractVo createMockVo() {
        Contract entity = createMockEntity();
        return entity.toVo();
    }

    @Override
    protected String getRedisKeyPrefix() {
        return "contract:";
    }

    @Override
    protected Integer getMockId() {
        return 1;
    }

    @Override
    protected String getSearchText() {
        return "测试";
    }

    /**
     * 测试ContractService特有的findById方法，验证实体-VO转换和缓存逻辑
     */
    @Test
    void testContractFindById() {
        Integer id = getMockId();
        Contract mockEntity = createMockEntity();
        ContractVo expectedVo = createMockVo();

        // 模拟Redis缓存未命中和服务方法调用
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
        when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity));

        // 执行方法
        ContractVo result = contractService.findById(id);

        // 验证结果
        assertNotNull(result);
        assertEquals(expectedVo.getId(), result.getId());
        assertEquals(expectedVo.getName(), result.getName());
        assertEquals(expectedVo.getCode(), result.getCode());

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

    /**
     * 测试ContractService特有的updateByVo方法，验证特定字段的更新逻辑
     */
    @Test
    void testContractUpdateByVo() {
        Integer id = getMockId();
        Contract mockEntity = createMockEntity();
        ContractVo mockVo = createMockVo();
        mockVo.setName("更新后的合同名称");
        mockVo.setAmount(20000.0);

        // 模拟getById方法调用
        when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity));
        when(contractRepository.save(any(Contract.class))).thenAnswer(i -> i.getArguments()[0]);

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

        // 验证结果 - 直接检查传入的实体对象是否被正确更新
        assertNotNull(mockEntity);
        assertEquals(mockVo.getName(), mockEntity.getName());
        assertEquals(mockVo.getAmount(), mockEntity.getAmount());
        // code字段应该没有被修改，因为我们使用的是传入的mockEntity的code
        assertEquals("CONTRACT-001", mockEntity.getCode()); // 未修改的字段保持不变

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

    /**
     * 测试ContractService的缓存刷新机制
     */
    @Test
    void testContractCacheRefresh() {
        Integer id = getMockId();
        Contract mockEntity1 = createMockEntity();
        Contract mockEntity2 = createMockEntity();
        mockEntity2.setName("刷新后的合同名称");

        // 第一次调用：缓存未命中
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
        when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity1));

        ContractVo result1 = contractService.findById(id);
        assertEquals(mockEntity1.getName(), result1.getName());

        // 模拟实体更新
        reset(redisTemplate);
        when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity2));
        when(contractService.findById(id)).thenCallRealMethod();
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);

        // 第二次调用：应该获取到更新后的数据
        ContractVo result2 = contractService.findById(id);
        assertEquals(mockEntity2.getName(), result2.getName());

        // 验证缓存被更新
        verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result2), anyLong(), any());
    }

    /**
     * 测试ContractService的批量操作后缓存清理
     */
    @Test
    void testContractBatchOperationCacheEviction() {
        // 模拟批量更新操作
        Contract mockEntity = createMockEntity();
        when(contractRepository.save(any(Contract.class))).thenReturn(mockEntity);

        // 执行保存操作
        contractService.save(mockEntity);

        // 验证缓存被清理
        verify(redisTemplate, times(1)).delete(anyString());
    }
}