package com.gameplatform.server.service.link;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gameplatform.server.mapper.agent.LinkTaskMapper;
import com.gameplatform.server.model.dto.device.DeviceStatusResponse;
import com.gameplatform.server.model.entity.agent.LinkTask;
import com.gameplatform.server.service.detection.GameCompletionDetectionService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

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

/**
 * DeviceTaskUpdateService 测试
 */
public class DeviceTaskUpdateServiceTest {

    private DeviceTaskUpdateService deviceTaskUpdateService;
    
    @Mock
    private LinkTaskMapper linkTaskMapper;
    
    @Mock
    private GameCompletionDetectionService gameCompletionDetectionService;
    
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        objectMapper = new ObjectMapper();
        deviceTaskUpdateService = new DeviceTaskUpdateService(linkTaskMapper, objectMapper, gameCompletionDetectionService);
    }

    @Test
    void testUpdateTaskByDeviceStatus_WithPoints() {
        // 准备测试数据
        DeviceStatusResponse.DeviceInfo deviceInfo = new DeviceStatusResponse.DeviceInfo();
        deviceInfo.setDeviceId("f1");
        deviceInfo.setVal("5300");
        deviceInfo.setAvailable(false);
        
        LinkTask task1 = createMockTask(1L, "ABC123");
        LinkTask task2 = createMockTask(2L, "DEF456");
        List<LinkTask> tasks = Arrays.asList(task1, task2);
        
        when(linkTaskMapper.findByMachineIdAndStatus("f1", "LOGGED_IN")).thenReturn(tasks);
        when(linkTaskMapper.updatePointsIfGreater(anyLong(), anyInt())).thenReturn(1);
        when(gameCompletionDetectionService.detectGameCompletion("f1", "5300", "EVENT_LISTENER")).thenReturn(false);
        
        // 执行测试
        deviceTaskUpdateService.updateTaskByDeviceStatus(deviceInfo);
        
        // 验证结果
        verify(gameCompletionDetectionService).detectGameCompletion("f1", "5300", "EVENT_LISTENER");
        verify(linkTaskMapper).findByMachineIdAndStatus("f1", "LOGGED_IN");
        verify(linkTaskMapper, times(2)).updatePointsIfGreater(anyLong(), eq(5300));
        
        // 验证任务状态仍为LOGGED_IN（点数更新通过数据库完成，不修改内存对象）
        assertEquals("LOGGED_IN", task1.getStatus());
        assertEquals("LOGGED_IN", task2.getStatus());
    }

    @Test
    void testUpdateTaskByDeviceStatus_Completed() {
        // 准备测试数据
        DeviceStatusResponse.DeviceInfo deviceInfo = new DeviceStatusResponse.DeviceInfo();
        deviceInfo.setDeviceId("f1");
        deviceInfo.setVal("已打完");
        deviceInfo.setAvailable(false);
        
        when(gameCompletionDetectionService.detectGameCompletion("f1", "已打完", "EVENT_LISTENER")).thenReturn(true);
        
        // 执行测试
        deviceTaskUpdateService.updateTaskByDeviceStatus(deviceInfo);
        
        // 验证结果 - 新逻辑中，完成检测由 GameCompletionDetectionService 处理
        verify(gameCompletionDetectionService).detectGameCompletion("f1", "已打完", "EVENT_LISTENER");
        // 由于 "已打完" 不是数字，不会调用 updateTaskPointsOnly
        verify(linkTaskMapper, never()).findByMachineIdAndStatus(anyString(), anyString());
    }

    @Test
    void testUpdateTaskByDeviceStatus_Idle() {
        // 准备测试数据
        DeviceStatusResponse.DeviceInfo deviceInfo = new DeviceStatusResponse.DeviceInfo();
        deviceInfo.setDeviceId("f1");
        deviceInfo.setVal("空闲");
        deviceInfo.setAvailable(true);
        
        when(gameCompletionDetectionService.detectGameCompletion("f1", "空闲", "EVENT_LISTENER")).thenReturn(false);
        
        // 执行测试
        deviceTaskUpdateService.updateTaskByDeviceStatus(deviceInfo);
        
        // 验证结果 - 新逻辑中，空闲状态由 GameCompletionDetectionService 处理
        verify(gameCompletionDetectionService).detectGameCompletion("f1", "空闲", "EVENT_LISTENER");
        // 由于 "空闲" 不是数字，不会调用 updateTaskPointsOnly
        verify(linkTaskMapper, never()).findByMachineIdAndStatus(anyString(), anyString());
    }

    @Test
    void testUpdateTaskByDeviceStatus_NoTasks() {
        // 准备测试数据
        DeviceStatusResponse.DeviceInfo deviceInfo = new DeviceStatusResponse.DeviceInfo();
        deviceInfo.setDeviceId("f1");
        deviceInfo.setVal("5300");
        deviceInfo.setAvailable(false);
        
        when(linkTaskMapper.findByMachineIdAndStatus("f1", "LOGGED_IN")).thenReturn(Arrays.asList());
        when(gameCompletionDetectionService.detectGameCompletion("f1", "5300", "EVENT_LISTENER")).thenReturn(false);
        
        // 执行测试
        deviceTaskUpdateService.updateTaskByDeviceStatus(deviceInfo);
        
        // 验证结果
        verify(gameCompletionDetectionService).detectGameCompletion("f1", "5300", "EVENT_LISTENER");
        verify(linkTaskMapper).findByMachineIdAndStatus("f1", "LOGGED_IN");
        verify(linkTaskMapper, never()).updatePointsIfGreater(anyLong(), anyInt());
    }

    @Test
    void testUpdateTaskByDeviceStatus_UnknownStatus() {
        // 准备测试数据
        DeviceStatusResponse.DeviceInfo deviceInfo = new DeviceStatusResponse.DeviceInfo();
        deviceInfo.setDeviceId("f1");
        deviceInfo.setVal("未知状态");
        deviceInfo.setAvailable(false);
        
        when(gameCompletionDetectionService.detectGameCompletion("f1", "未知状态", "EVENT_LISTENER")).thenReturn(false);
        
        // 执行测试
        deviceTaskUpdateService.updateTaskByDeviceStatus(deviceInfo);
        
        // 验证结果 - 新逻辑中，所有状态都会通过 GameCompletionDetectionService 处理
        verify(gameCompletionDetectionService).detectGameCompletion("f1", "未知状态", "EVENT_LISTENER");
        // 由于 "未知状态" 不是数字，不会调用 updateTaskPointsOnly
        verify(linkTaskMapper, never()).findByMachineIdAndStatus(anyString(), anyString());
    }

    private LinkTask createMockTask(Long id, String codeNo) {
        LinkTask task = new LinkTask();
        task.setId(id);
        task.setCodeNo(codeNo);
        task.setStatus("LOGGED_IN");
        task.setCreatedAt(LocalDateTime.now());
        return task;
    }
}
