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

import cn.hutool.core.bean.BeanUtil;
import com.ycwl.basic.puzzle.dto.PuzzleTemplateDTO;
import com.ycwl.basic.puzzle.dto.TemplateCreateRequest;
import com.ycwl.basic.puzzle.entity.PuzzleElementEntity;
import com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity;
import com.ycwl.basic.puzzle.mapper.PuzzleElementMapper;
import com.ycwl.basic.puzzle.mapper.PuzzleTemplateMapper;
import com.ycwl.basic.puzzle.repository.PuzzleRepository;
import com.ycwl.basic.puzzle.test.PuzzleTestDataBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;

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

/**
 * PuzzleTemplateService单元测试
 * 使用Mockito模拟数据库操作，不依赖外部数据库
 *
 * @author Claude
 * @since 2025-01-17
 */
@ExtendWith(MockitoExtension.class)
class PuzzleTemplateServiceImplTest {

    @Mock
    private PuzzleTemplateMapper templateMapper;

    @Mock
    private PuzzleElementMapper elementMapper;

    @Mock
    private PuzzleRepository puzzleRepository;

    @InjectMocks
    private PuzzleTemplateServiceImpl templateService;

    private PuzzleTemplateEntity mockTemplate;
    private List<PuzzleElementEntity> mockElements;

    @BeforeEach
    void setUp() {
        mockTemplate = PuzzleTestDataBuilder.createBasicTemplate();
        mockElements = PuzzleTestDataBuilder.createFullTestElements(mockTemplate.getId());
    }

    @Test
    void testCreateTemplate_Success() {
        // Given
        TemplateCreateRequest request = new TemplateCreateRequest();
        request.setCode("new_template");
        request.setName("新模板");
        request.setCanvasWidth(800);
        request.setCanvasHeight(1200);
        request.setBackgroundType(0);
        request.setBackgroundColor("#FFFFFF");

        when(templateMapper.countByCode(eq("new_template"), isNull())).thenReturn(0);
        when(templateMapper.insert(any(PuzzleTemplateEntity.class))).thenAnswer(invocation -> {
            PuzzleTemplateEntity entity = invocation.getArgument(0);
            entity.setId(100L);
            return 1;
        });

        // When
        Long templateId = templateService.createTemplate(request);

        // Then
        assertNotNull(templateId);
        assertEquals(100L, templateId);
        verify(templateMapper).countByCode(eq("new_template"), isNull());
        verify(templateMapper).insert(any(PuzzleTemplateEntity.class));
    }

    @Test
    void testCreateTemplate_DuplicateCode() {
        // Given
        TemplateCreateRequest request = new TemplateCreateRequest();
        request.setCode("existing_code");
        request.setName("重复编码模板");

        when(templateMapper.countByCode(eq("existing_code"), isNull())).thenReturn(1);

        // When & Then
        IllegalArgumentException exception = assertThrows(
                IllegalArgumentException.class,
                () -> templateService.createTemplate(request)
        );
        assertTrue(exception.getMessage().contains("模板编码已存在"));
        verify(templateMapper).countByCode(eq("existing_code"), isNull());
        verify(templateMapper, never()).insert(any());
    }

    @Test
    void testGetTemplateDetail_Success() {
        // Given
        when(templateMapper.getById(1L)).thenReturn(mockTemplate);
        when(elementMapper.getByTemplateId(1L)).thenReturn(mockElements);

        // When
        PuzzleTemplateDTO result = templateService.getTemplateDetail(1L);

        // Then
        assertNotNull(result);
        assertEquals(mockTemplate.getId(), result.getId());
        assertEquals(mockTemplate.getCode(), result.getCode());
        assertEquals(mockTemplate.getName(), result.getName());
        assertNotNull(result.getElements());
        assertEquals(mockElements.size(), result.getElements().size());
        verify(templateMapper).getById(1L);
        verify(elementMapper).getByTemplateId(1L);
    }

    @Test
    void testGetTemplateDetail_NotFound() {
        // Given
        when(templateMapper.getById(999L)).thenReturn(null);

        // When & Then
        IllegalArgumentException exception = assertThrows(
                IllegalArgumentException.class,
                () -> templateService.getTemplateDetail(999L)
        );
        assertTrue(exception.getMessage().contains("模板不存在"));
        verify(templateMapper).getById(999L);
    }

    @Test
    void testGetTemplateByCode_Success() {
        // Given
        when(templateMapper.getByCode("test_template")).thenReturn(mockTemplate);
        when(elementMapper.getByTemplateId(1L)).thenReturn(mockElements);

        // When
        PuzzleTemplateDTO result = templateService.getTemplateByCode("test_template");

        // Then
        assertNotNull(result);
        assertEquals("test_template", result.getCode());
        assertNotNull(result.getElements());
        verify(templateMapper).getByCode("test_template");
        verify(elementMapper).getByTemplateId(1L);
    }

    @Test
    void testUpdateTemplate_Success() {
        // Given
        TemplateCreateRequest request = new TemplateCreateRequest();
        request.setName("更新后的名称");
        request.setStatus(0);

        when(templateMapper.getById(1L)).thenReturn(mockTemplate);
        when(templateMapper.update(any(PuzzleTemplateEntity.class))).thenReturn(1);

        // When
        templateService.updateTemplate(1L, request);

        // Then
        verify(templateMapper).getById(1L);
        verify(templateMapper).update(any(PuzzleTemplateEntity.class));
    }

    @Test
    void testDeleteTemplate_Success() {
        // Given
        when(templateMapper.getById(1L)).thenReturn(mockTemplate);
        when(templateMapper.deleteById(1L)).thenReturn(1);
        when(elementMapper.deleteByTemplateId(1L)).thenReturn(mockElements.size());

        // When
        templateService.deleteTemplate(1L);

        // Then
        verify(templateMapper).getById(1L);
        verify(templateMapper).deleteById(1L);
        verify(elementMapper).deleteByTemplateId(1L);
    }

    @Test
    void testListTemplates_Success() {
        // Given
        List<PuzzleTemplateEntity> templates = List.of(mockTemplate);
        when(templateMapper.list(null, "test", 1)).thenReturn(templates);
        when(elementMapper.getByTemplateId(anyLong())).thenReturn(mockElements);

        // When
        List<PuzzleTemplateDTO> result = templateService.listTemplates(null, "test", 1);

        // Then
        assertNotNull(result);
        assertEquals(1, result.size());
        assertEquals(mockTemplate.getCode(), result.get(0).getCode());
        verify(templateMapper).list(null, "test", 1);
    }
}
