package com.ycwl.basic.puzzle.service.impl;

import com.ycwl.basic.puzzle.dto.PuzzleFillRuleDTO;
import com.ycwl.basic.puzzle.dto.PuzzleFillRuleItemDTO;
import com.ycwl.basic.puzzle.dto.PuzzleFillRuleSaveRequest;
import com.ycwl.basic.puzzle.entity.PuzzleFillRuleEntity;
import com.ycwl.basic.puzzle.entity.PuzzleFillRuleItemEntity;
import com.ycwl.basic.puzzle.mapper.PuzzleFillRuleItemMapper;
import com.ycwl.basic.puzzle.mapper.PuzzleFillRuleMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.*;

/**
 * 拼图填充规则服务测试
 */
@DisplayName("拼图填充规则服务测试")
@ExtendWith(MockitoExtension.class)
class PuzzleFillRuleServiceImplTest {

    @Mock
    private PuzzleFillRuleMapper ruleMapper;

    @Mock
    private PuzzleFillRuleItemMapper itemMapper;

    @InjectMocks
    private PuzzleFillRuleServiceImpl service;

    @BeforeEach
    void setUp() {
    }

    @Test
    @DisplayName("创建规则应该保存主规则和明细")
    void shouldCreateRuleWithItems() {
        // Given
        PuzzleFillRuleSaveRequest request = createSaveRequest(null);
        request.setItems(Arrays.asList(
                createItemDTO("sfp_1", 1),
                createItemDTO("sfp_2", 2)
        ));

        // 模拟insert返回的ID
        doAnswer(invocation -> {
            PuzzleFillRuleEntity entity = invocation.getArgument(0);
            entity.setId(100L);
            return 1;
        }).when(ruleMapper).insert(any(PuzzleFillRuleEntity.class));

        when(itemMapper.batchInsert(anyList())).thenReturn(2);

        // When
        Long ruleId = service.create(request);

        // Then
        assertEquals(100L, ruleId);

        // 验证主规则保存
        verify(ruleMapper, times(1)).insert(any(PuzzleFillRuleEntity.class));

        // 验证明细批量保存
        ArgumentCaptor<List<PuzzleFillRuleItemEntity>> itemsCaptor = ArgumentCaptor.forClass(List.class);
        verify(itemMapper, times(1)).batchInsert(itemsCaptor.capture());

        List<PuzzleFillRuleItemEntity> savedItems = itemsCaptor.getValue();
        assertEquals(2, savedItems.size());
        assertEquals(100L, savedItems.get(0).getRuleId());
        assertEquals(100L, savedItems.get(1).getRuleId());
    }

    @Test
    @DisplayName("创建规则时如果没有明细应该只保存主规则")
    void shouldCreateRuleWithoutItems() {
        // Given
        PuzzleFillRuleSaveRequest request = createSaveRequest(null);
        request.setItems(null);

        doAnswer(invocation -> {
            PuzzleFillRuleEntity entity = invocation.getArgument(0);
            entity.setId(100L);
            return 1;
        }).when(ruleMapper).insert(any(PuzzleFillRuleEntity.class));

        // When
        Long ruleId = service.create(request);

        // Then
        assertEquals(100L, ruleId);
        verify(ruleMapper, times(1)).insert(any(PuzzleFillRuleEntity.class));
        verify(itemMapper, never()).batchInsert(anyList());
    }

    @Test
    @DisplayName("更新规则应该先删除旧明细再插入新明细")
    void shouldUpdateRuleByDeletingOldItemsAndInsertingNew() {
        // Given
        PuzzleFillRuleSaveRequest request = createSaveRequest(10L);
        request.setItems(Arrays.asList(
                createItemDTO("sfp_1", 1),
                createItemDTO("sfp_2", 2)
        ));

        when(ruleMapper.updateById(any(PuzzleFillRuleEntity.class))).thenReturn(1);
        when(itemMapper.deleteByRuleId(10L)).thenReturn(2);
        when(itemMapper.batchInsert(anyList())).thenReturn(2);

        // When
        Boolean result = service.update(request);

        // Then
        assertTrue(result);

        // 验证更新主规则
        verify(ruleMapper, times(1)).updateById(any(PuzzleFillRuleEntity.class));

        // 验证删除旧明细
        verify(itemMapper, times(1)).deleteByRuleId(10L);

        // 验证插入新明细
        verify(itemMapper, times(1)).batchInsert(anyList());
    }

    @Test
    @DisplayName("更新规则时ID为null应该抛出异常")
    void shouldThrowExceptionWhenUpdateWithNullId() {
        // Given
        PuzzleFillRuleSaveRequest request = createSaveRequest(null);

        // When & Then
        assertThrows(IllegalArgumentException.class, () -> service.update(request));
        verify(ruleMapper, never()).updateById(any());
    }

    @Test
    @DisplayName("删除规则应该级联删除明细")
    void shouldDeleteRuleWithCascade() {
        // Given
        Long ruleId = 10L;
        when(ruleMapper.deleteById(ruleId)).thenReturn(1);

        // When
        Boolean result = service.delete(ruleId);

        // Then
        assertTrue(result);
        verify(ruleMapper, times(1)).deleteById(ruleId);
        // 明细由数据库外键级联删除,不需要手动删除
    }

    @Test
    @DisplayName("查询单条规则应该包含明细")
    void shouldGetRuleByIdWithItems() {
        // Given
        Long ruleId = 10L;

        PuzzleFillRuleEntity ruleEntity = createRuleEntity(ruleId);
        when(ruleMapper.selectById(ruleId)).thenReturn(ruleEntity);

        List<PuzzleFillRuleItemEntity> itemEntities = Arrays.asList(
                createItemEntity(1L, ruleId, "sfp_1"),
                createItemEntity(2L, ruleId, "sfp_2")
        );
        when(itemMapper.listByRuleId(ruleId)).thenReturn(itemEntities);

        // When
        PuzzleFillRuleDTO dto = service.getById(ruleId);

        // Then
        assertNotNull(dto);
        assertEquals(ruleId, dto.getId());
        assertEquals("测试规则", dto.getRuleName());
        assertNotNull(dto.getItems());
        assertEquals(2, dto.getItems().size());
        assertEquals("sfp_1", dto.getItems().get(0).getElementKey());
        assertEquals("sfp_2", dto.getItems().get(1).getElementKey());
    }

    @Test
    @DisplayName("查询单条规则时规则不存在应该返回null")
    void shouldReturnNullWhenRuleNotFound() {
        // Given
        Long ruleId = 999L;
        when(ruleMapper.selectById(ruleId)).thenReturn(null);

        // When
        PuzzleFillRuleDTO dto = service.getById(ruleId);

        // Then
        assertNull(dto);
        verify(itemMapper, never()).listByRuleId(anyLong());
    }

    @Test
    @DisplayName("查询模板的所有规则应该包含明细")
    void shouldListRulesByTemplateIdWithItems() {
        // Given
        Long templateId = 1L;

        List<PuzzleFillRuleEntity> ruleEntities = Arrays.asList(
                createRuleEntity(1L),
                createRuleEntity(2L)
        );
        when(ruleMapper.listByTemplateId(templateId)).thenReturn(ruleEntities);

        when(itemMapper.listByRuleId(1L)).thenReturn(Arrays.asList(
                createItemEntity(1L, 1L, "sfp_1")
        ));
        when(itemMapper.listByRuleId(2L)).thenReturn(Arrays.asList(
                createItemEntity(2L, 2L, "sfp_2")
        ));

        // When
        List<PuzzleFillRuleDTO> dtos = service.listByTemplateId(templateId);

        // Then
        assertEquals(2, dtos.size());
        assertEquals(1L, dtos.get(0).getId());
        assertEquals(1, dtos.get(0).getItems().size());
        assertEquals(2L, dtos.get(1).getId());
        assertEquals(1, dtos.get(1).getItems().size());

        verify(itemMapper, times(1)).listByRuleId(1L);
        verify(itemMapper, times(1)).listByRuleId(2L);
    }

    @Test
    @DisplayName("启用/禁用规则应该更新状态")
    void shouldToggleEnabled() {
        // Given
        Long ruleId = 10L;
        Integer enabled = 1;

        when(ruleMapper.update(any(), any())).thenReturn(1);

        // When
        Boolean result = service.toggleEnabled(ruleId, enabled);

        // Then
        assertTrue(result);
        verify(ruleMapper, times(1)).update(any(), any());
    }

    // 辅助方法
    private PuzzleFillRuleSaveRequest createSaveRequest(Long id) {
        PuzzleFillRuleSaveRequest request = new PuzzleFillRuleSaveRequest();
        request.setId(id);
        request.setTemplateId(1L);
        request.setRuleName("测试规则");
        request.setConditionType("DEVICE_COUNT");
        request.setConditionValue("{\"deviceCount\": 4}");
        request.setPriority(100);
        request.setEnabled(1);
        request.setDescription("测试描述");
        return request;
    }

    private PuzzleFillRuleItemDTO createItemDTO(String elementKey, int order) {
        PuzzleFillRuleItemDTO dto = new PuzzleFillRuleItemDTO();
        dto.setElementKey(elementKey);
        dto.setDataSource("DEVICE_IMAGE");
        dto.setSourceFilter("{\"deviceIndex\": 0}");
        dto.setSortStrategy("LATEST");
        dto.setItemOrder(order);
        return dto;
    }

    private PuzzleFillRuleEntity createRuleEntity(Long id) {
        PuzzleFillRuleEntity entity = new PuzzleFillRuleEntity();
        entity.setId(id);
        entity.setTemplateId(1L);
        entity.setRuleName("测试规则");
        entity.setConditionType("DEVICE_COUNT");
        entity.setConditionValue("{\"deviceCount\": 4}");
        entity.setPriority(100);
        entity.setEnabled(1);
        return entity;
    }

    private PuzzleFillRuleItemEntity createItemEntity(Long id, Long ruleId, String elementKey) {
        PuzzleFillRuleItemEntity entity = new PuzzleFillRuleItemEntity();
        entity.setId(id);
        entity.setRuleId(ruleId);
        entity.setElementKey(elementKey);
        entity.setDataSource("DEVICE_IMAGE");
        entity.setSourceFilter("{\"deviceIndex\": 0}");
        entity.setSortStrategy("LATEST");
        entity.setItemOrder(id.intValue());
        return entity;
    }
}
