package com.ecep.contract.ds.sale.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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
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 java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
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.ds.contract.repository.SalesOrderRepository;
import com.ecep.contract.ds.contract.service.SaleOrdersService;
import com.ecep.contract.ds.customer.model.SalesOrder;
import com.ecep.contract.service.BaseServiceTest;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.vo.SalesOrderVo;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * SaleOrdersService的集成测试类，用于验证SaleOrdersService的实体-VO转换和缓存逻辑
 */
@SpringBootTest
public class SaleOrdersServiceIntegrationTest extends BaseServiceTest<SalesOrder, SalesOrderVo> {

    @Mock
    private SalesOrderRepository repository;

    @MockBean
    private RedisTemplate<String, Object> redisTemplate;

    @InjectMocks
    private SaleOrdersService service;

    private ObjectMapper objectMapper;

    @Override
    protected IEntityService<SalesOrder> getEntityService() {
        return service;
    }

    @Override
    protected QueryService<SalesOrderVo> getQueryService() {
        return service;
    }

    @Override
    protected VoableService<SalesOrder, SalesOrderVo> getVoableService() {
        return service;
    }

    @Override
    protected SalesOrder createMockEntity() {
        SalesOrder salesOrder = new SalesOrder();
        salesOrder.setId(getMockId());
        salesOrder.setCode("SO-TEST001");
        salesOrder.setMakerDate(LocalDate.now());
        return salesOrder;
    }

    @Override
    protected SalesOrderVo createMockVo() {
        SalesOrder entity = createMockEntity();
        SalesOrderVo vo = new SalesOrderVo();
        vo.setId(entity.getId());
        vo.setCode(entity.getCode());
        // 复制其他需要的属性
        return vo;
    }

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

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

    @Override
    protected String getSearchText() {
        return "SO-TEST";
    }

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

    /**
     * 测试findById方法，验证实体-VO转换和缓存逻辑
     */
    @Test
    public void testFindById() {
        Integer id = getMockId();
        SalesOrder mockEntity = createMockEntity();
        SalesOrderVo expectedVo = createMockVo();

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

        // 执行方法
        SalesOrderVo result = service.findById(id);

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

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

    /**
     * 测试findById方法，验证当实体不存在时返回null
     */
    @Test
    public void testFindById_EntityNotFound() {
        Integer id = 999;

        // 配置mock行为
        when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
        when(repository.findById(id)).thenReturn(Optional.empty());

        // 执行方法
        SalesOrderVo result = service.findById(id);

        // 验证结果
        assertNull(result);

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

    /**
     * 测试save方法，验证保存逻辑和缓存清理
     */
    @Test
    public void testSave() {
        SalesOrder mockEntity = createMockEntity();

        // 配置mock行为
        when(repository.save(any(SalesOrder.class))).thenReturn(mockEntity);

        // 执行方法
        SalesOrder result = service.save(mockEntity);

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

        // 验证方法调用
        verify(repository, times(1)).save(mockEntity);
        // 验证缓存清理
        verify(redisTemplate, times(1)).delete(anyString());
    }

    /**
     * 测试delete方法，验证删除逻辑和缓存清理
     */
    @Test
    public void testDelete() {
        SalesOrder mockEntity = createMockEntity();

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

        // 验证方法调用
        verify(repository, times(1)).delete(mockEntity);
        // 验证缓存清理
        verify(redisTemplate, times(1)).delete(getRedisKeyPrefix() + mockEntity.getId());
    }

    /**
     * 测试findAll方法，验证分页功能和VO转换
     */
    @Test
    public void testFindAll() {
        // 准备测试数据
        List<SalesOrder> salesOrders = Arrays.asList(createMockEntity());
        Pageable pageable = PageRequest.of(0, 10);
        Page<SalesOrder> pageResult = new PageImpl<>(salesOrders, pageable, salesOrders.size());

        // 配置repository.findAll的mock行为
        doReturn(pageResult).when(repository).findAll(any(Specification.class), any(Pageable.class));
        
        // 执行方法 - 使用空的JsonNode
        JsonNode emptyParamsNode = objectMapper.createObjectNode();
        
        try {
            // 尝试正常执行方法
            Page<SalesOrderVo> result = service.findAll(emptyParamsNode, pageable);
            
            // 验证结果
            assertNotNull(result);
            assertEquals(1, result.getTotalElements());
            
            // 验证方法调用
            verify(repository, times(1)).findAll(any(Specification.class), any(Pageable.class));
        } catch (Exception e) {
            // 如果发生异常，记录并断言失败
            e.printStackTrace();
            assertNull("Method should not throw exception: " + e.getMessage());
        }
    }

    /**
     * 测试带参数的findAll方法，验证参数处理
     */
    @Test
    public void testFindAll_WithParams() throws Exception {
        // 准备测试数据
        List<SalesOrder> salesOrders = Arrays.asList(createMockEntity());
        Pageable pageable = PageRequest.of(0, 10);
        Page<SalesOrder> pageResult = new PageImpl<>(salesOrders, pageable, salesOrders.size());

        // 创建带搜索参数的JSON
        String paramsJson = "{\"searchText\": \"测试\"}";
        JsonNode paramsNode = objectMapper.readTree(paramsJson);

        // 配置mock行为
        when(repository.findAll(any(Specification.class), eq(pageable))).thenReturn(pageResult);

        // 执行方法
        Page<SalesOrderVo> result = service.findAll(paramsNode, pageable);

        // 验证结果
        assertNotNull(result);
        assertEquals(1, result.getTotalElements());

        // 验证方法调用
        verify(repository, times(1)).findAll(any(Specification.class), eq(pageable));
    }

    /**
     * 测试SaleOrdersService的缓存刷新机制
     */
    @Test
    void testSaleOrdersCacheRefresh() {
        Integer id = getMockId();
        SalesOrder mockEntity1 = createMockEntity();
        SalesOrder mockEntity2 = createMockEntity();
        mockEntity2.setCode("SO-TEST002");

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

        SalesOrderVo result1 = service.findById(id);
        assertEquals(mockEntity1.getCode(), result1.getCode());

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

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

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

    /**
     * 测试SaleOrdersService的缓存命中情况
     */
    @Test
    public void testFindById_CacheHit() {
        Integer id = getMockId();
        SalesOrderVo expectedVo = createMockVo();

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

        // 执行方法
        SalesOrderVo result = service.findById(id);

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

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