package com.ycwl.basic.puzzle.fill.condition;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

/**
 * 机位ID匹配条件策略测试
 */
@DisplayName("机位ID匹配条件策略测试")
class DeviceIdMatchConditionStrategyTest {

    private DeviceIdMatchConditionStrategy strategy;
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        strategy = new DeviceIdMatchConditionStrategy();
        objectMapper = new ObjectMapper();
    }

    @Test
    @DisplayName("应该返回正确的支持类型")
    void shouldReturnCorrectSupportedType() {
        assertEquals("DEVICE_ID_MATCH", strategy.getSupportedType());
    }

    @Test
    @DisplayName("单机位匹配-机位存在应该返回true")
    void shouldReturnTrueWhenSingleDeviceIdExists() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceId\": 200}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("单机位匹配-机位不存在应该返回false")
    void shouldReturnFalseWhenSingleDeviceIdNotExists() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceId\": 999}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("多机位ANY匹配-至少存在一个机位应该返回true")
    void shouldReturnTrueWhenAnyDeviceIdMatches() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        // 200存在,400不存在,但ANY模式只需一个匹配
        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [200, 400], \"matchMode\": \"ANY\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("多机位ANY匹配-没有任何机位存在应该返回false")
    void shouldReturnFalseWhenNoDeviceIdMatches() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [400, 500], \"matchMode\": \"ANY\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("多机位ALL匹配-所有机位都存在应该返回true")
    void shouldReturnTrueWhenAllDeviceIdsMatch() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [100, 200], \"matchMode\": \"ALL\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("多机位ALL匹配-部分机位不存在应该返回false")
    void shouldReturnFalseWhenNotAllDeviceIdsMatch() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        // 200存在但400不存在
        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [200, 400], \"matchMode\": \"ALL\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("多机位匹配-未指定matchMode应该默认使用ANY模式")
    void shouldDefaultToAnyModeWhenMatchModeNotSpecified() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        // 未指定matchMode,默认ANY,200存在即可
        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [200, 400]}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("多机位匹配-matchMode不区分大小写")
    void shouldBeCaseInsensitiveForMatchMode() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        // 使用小写all
        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [100, 200], \"matchMode\": \"all\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("当conditionValue为null时应该返回false")
    void shouldReturnFalseWhenConditionValueIsNull() {
        // Given
        ConditionContext context = ConditionContext.builder()
                .deviceIds(Arrays.asList(100L, 200L))
                .build();

        // When
        boolean result = strategy.evaluate(null, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("当上下文中机位列表为null时应该返回false")
    void shouldReturnFalseWhenContextDeviceIdsIsNull() throws Exception {
        // Given
        ConditionContext context = ConditionContext.builder()
                .deviceIds(null)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceId\": 100}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("当上下文中机位列表为空时应该返回false")
    void shouldReturnFalseWhenContextDeviceIdsIsEmpty() throws Exception {
        // Given
        ConditionContext context = ConditionContext.builder()
                .deviceIds(Collections.emptyList())
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceId\": 100}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("当deviceIds不是数组时应该返回false")
    void shouldReturnFalseWhenDeviceIdsIsNotArray() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        // deviceIds不是数组而是字符串
        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": \"100,200\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("当deviceIds数组为空时应该返回false")
    void shouldReturnFalseWhenDeviceIdsArrayIsEmpty() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": []}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("当缺少deviceId和deviceIds字段时应该返回false")
    void shouldReturnFalseWhenBothFieldsAreMissing() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"otherField\": \"test\"}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertFalse(result);
    }

    @Test
    @DisplayName("应该支持单个机位匹配边界情况-机位ID为0")
    void shouldSupportZeroDeviceId() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(0L, 100L, 200L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceId\": 0}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("应该支持大数值机位ID")
    void shouldSupportLargeDeviceId() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(999999999999L, 100L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceId\": 999999999999}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }

    @Test
    @DisplayName("多机位匹配-单个元素的数组应该正常工作")
    void shouldWorkWithSingleElementArray() throws Exception {
        // Given
        List<Long> deviceIds = Arrays.asList(100L, 200L, 300L);
        ConditionContext context = ConditionContext.builder()
                .deviceIds(deviceIds)
                .build();

        JsonNode conditionValue = objectMapper.readTree("{\"deviceIds\": [200]}");

        // When
        boolean result = strategy.evaluate(conditionValue, context);

        // Then
        assertTrue(result);
    }
}
