package com.flowable.devops.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.flowable.devops.entity.NodeType;
import com.flowable.devops.repository.NodeTypeRepository;
import com.flowable.devops.workflow.node.registry.NodeTypeRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

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

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

/**
 * NodeTypeService单元测试（不依赖Spring Context）
 */
@ExtendWith(MockitoExtension.class)
public class NodeTypeServiceSimpleTest {

    @Mock
    private NodeTypeRepository nodeTypeRepository;
    
    @Mock
    private NodeTypeRegistry nodeTypeRegistry;

    private NodeTypeService nodeTypeService;
    private ObjectMapper objectMapper;

    @BeforeEach
    public void setUp() {
        nodeTypeService = new NodeTypeService();
        objectMapper = new ObjectMapper();
        
        // 使用反射设置私有字段
        try {
            var nodeTypeRepoField = NodeTypeService.class.getDeclaredField("nodeTypeRepository");
            nodeTypeRepoField.setAccessible(true);
            nodeTypeRepoField.set(nodeTypeService, nodeTypeRepository);
            
            var nodeTypeRegistryField = NodeTypeService.class.getDeclaredField("nodeTypeRegistry");
            nodeTypeRegistryField.setAccessible(true);
            nodeTypeRegistryField.set(nodeTypeService, nodeTypeRegistry);
        } catch (Exception e) {
            fail("设置测试字段失败: " + e.getMessage());
        }
    }

    @Test
    public void testGetAllNodeTypes() {
        // 准备测试数据
        NodeType nodeType1 = createTestNodeType("test1", "测试节点1", NodeType.NodeCategory.API);
        NodeType nodeType2 = createTestNodeType("test2", "测试节点2", NodeType.NodeCategory.LOGIC);
        
        List<NodeType> mockNodeTypes = Arrays.asList(nodeType1, nodeType2);
        
        // 配置mock行为
        when(nodeTypeRepository.findAllByOrderByDisplayOrderAscIdAsc()).thenReturn(mockNodeTypes);
        
        // 执行测试
        List<NodeType> result = nodeTypeService.getAllNodeTypes();
        
        // 验证结果
        assertNotNull(result);
        assertEquals(2, result.size());
        assertEquals("test1", result.get(0).getId());
        assertEquals("test2", result.get(1).getId());
        
        // 验证方法被调用
        verify(nodeTypeRepository).findAllByOrderByDisplayOrderAscIdAsc();
    }

    @Test
    public void testGetNodeTypeById() {
        // 准备测试数据
        String nodeTypeId = "test-node";
        NodeType expectedNodeType = createTestNodeType(nodeTypeId, "测试节点", NodeType.NodeCategory.OTHER);
        
        // 配置mock行为
        when(nodeTypeRepository.findById(nodeTypeId)).thenReturn(Optional.of(expectedNodeType));
        
        // 执行测试
        NodeType result = nodeTypeService.getNodeType(nodeTypeId);
        
        // 验证结果
        assertNotNull(result);
        assertEquals(nodeTypeId, result.getId());
        assertEquals("测试节点", result.getName());
        assertEquals(NodeType.NodeCategory.OTHER, result.getCategory());
        
        // 验证方法被调用
        verify(nodeTypeRepository).findById(nodeTypeId);
    }

    @Test
    public void testGetNodeTypeById_NotFound() {
        // 准备测试数据
        String nodeTypeId = "non-existent";
        
        // 配置mock行为
        when(nodeTypeRepository.findById(nodeTypeId)).thenReturn(Optional.empty());
        
        // 执行测试并验证异常
        assertThrows(NodeTypeNotFoundException.class, () -> {
            nodeTypeService.getNodeType(nodeTypeId);
        });
        
        // 验证方法被调用
        verify(nodeTypeRepository).findById(nodeTypeId);
    }

    @Test
    public void testGetStatistics() {
        // 配置mock行为
        when(nodeTypeRepository.count()).thenReturn(10L);
        when(nodeTypeRepository.countByEnabledTrue()).thenReturn(8L);
        when(nodeTypeRepository.countByCategory()).thenReturn(Arrays.asList(
            new Object[]{"API", 3L},
            new Object[]{"LOGIC", 2L},
            new Object[]{"OTHER", 5L}
        ));
        
        // 执行测试
        NodeTypeService.NodeTypeStatistics stats = nodeTypeService.getStatistics();
        
        // 验证结果
        assertNotNull(stats);
        assertEquals(10L, stats.getTotalCount());
        assertEquals(8L, stats.getEnabledCount());
        assertEquals(2L, stats.getDisabledCount());
        
        assertNotNull(stats.getCategoryStats());
        assertEquals(3, stats.getCategoryStats().size());
        assertEquals(3L, stats.getCategoryStats().get("API"));
        assertEquals(2L, stats.getCategoryStats().get("LOGIC"));
        assertEquals(5L, stats.getCategoryStats().get("OTHER"));
        
        // 验证方法被调用
        verify(nodeTypeRepository).count();
        verify(nodeTypeRepository).countByEnabledTrue();
        verify(nodeTypeRepository).countByCategory();
    }

    @Test
    public void testCreateNodeType() throws Exception {
        // 准备测试数据
        NodeType nodeType = createTestNodeType("new-node", "新节点", NodeType.NodeCategory.DATABASE);
        nodeType.setFields(objectMapper.readTree("[{\"name\":\"test\",\"type\":\"text\"}]"));
        nodeType.setOutputSchema(objectMapper.readTree("{\"type\":\"object\"}"));
        nodeType.setDisplayOrder(null); // 清空displayOrder以便测试自动分配逻辑
        
        // 配置mock行为
        when(nodeTypeRepository.existsById("new-node")).thenReturn(false);
        when(nodeTypeRepository.findMaxDisplayOrderByCategory(NodeType.NodeCategory.DATABASE)).thenReturn(10);
        when(nodeTypeRepository.save(any(NodeType.class))).thenAnswer(invocation -> {
            NodeType saved = invocation.getArgument(0);
            // 模拟设置创建时间等
            return saved;
        });
        
        // 执行测试
        NodeType result = nodeTypeService.createNodeType(nodeType);
        
        // 验证结果
        assertNotNull(result);
        assertEquals("new-node", result.getId());
        assertEquals("新节点", result.getName());
        assertEquals(NodeType.NodeCategory.DATABASE, result.getCategory());
        assertEquals(20, result.getDisplayOrder()); // 10 + 10
        
        // 验证方法被调用
        verify(nodeTypeRepository).existsById("new-node");
        verify(nodeTypeRepository).findMaxDisplayOrderByCategory(NodeType.NodeCategory.DATABASE);
        verify(nodeTypeRepository).save(nodeType);
        verify(nodeTypeRegistry).registerFromDatabase(result);
    }

    @Test
    public void testCreateNodeType_DuplicateId() {
        // 准备测试数据
        NodeType nodeType = createTestNodeType("existing-node", "已存在节点", NodeType.NodeCategory.API);
        
        // 配置mock行为
        when(nodeTypeRepository.existsById("existing-node")).thenReturn(true);
        
        // 执行测试并验证异常
        assertThrows(NodeTypeServiceException.class, () -> {
            nodeTypeService.createNodeType(nodeType);
        });
        
        // 验证方法被调用
        verify(nodeTypeRepository).existsById("existing-node");
        verify(nodeTypeRepository, never()).save(any(NodeType.class));
    }

    /**
     * 创建测试用的NodeType对象
     */
    private NodeType createTestNodeType(String id, String name, NodeType.NodeCategory category) {
        NodeType nodeType = new NodeType();
        nodeType.setId(id);
        nodeType.setName(name);
        nodeType.setDisplayName(name);
        nodeType.setCategory(category);
        nodeType.setDescription("测试用节点类型");
        nodeType.setIcon("test-icon");
        nodeType.setEnabled(true);
        nodeType.setDisplayOrder(10);
        nodeType.setImplementationClass("com.test.TestNode");
        return nodeType;
    }
}