package com.qaup.collision.geofence.service;

import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.common.model.spatial.AirportArea;
import com.qaup.collision.common.service.AirportAreaService;
import com.qaup.collision.geofence.model.dto.GeofenceAccessCheckResult;
import com.qaup.collision.rule.model.enums.RuleCategory;
import com.qaup.collision.rule.service.SpatialRuleService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import com.qaup.collision.websocket.event.GeofenceAlertWebSocketEvent;
import org.mockito.ArgumentCaptor;

/**
 * 无人车电子围栏服务测试
 */
@ExtendWith(MockitoExtension.class)
class UnmannedVehicleGeofenceServiceTest {

    @Mock
    private AirportAreaService airportAreaService;

    @Mock
    private SpatialRuleService spatialRuleService;

    @Mock
    private ApplicationEventPublisher eventPublisher;

    private UnmannedVehicleGeofenceService geofenceService;
    private GeometryFactory geometryFactory;

    @BeforeEach
    void setUp() {
        geofenceService = new UnmannedVehicleGeofenceService();
        ReflectionTestUtils.setField(geofenceService, "airportAreaService", airportAreaService);
        ReflectionTestUtils.setField(geofenceService, "spatialRuleService", spatialRuleService);
        ReflectionTestUtils.setField(geofenceService, "eventPublisher", eventPublisher);
        
        geometryFactory = new GeometryFactory();
    }

    @Test
    void testCheckAreaAccess_UnmannedVehicle_AreaExists_NoRules_ShouldAllow() {
        // Arrange
        String areaCode = "TEST_AREA_01";
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject unmannedVehicle = MovingObject.builder()
                .objectId("UV001")
                .objectType(MovingObjectType.UNMANNED_VEHICLE)
                .objectName("测试无人车")
                .currentPosition(position)
                .currentSpeed(10.0)
                .build();

        AirportArea area = new AirportArea();
        area.setAreaId(areaCode);
        area.setName("测试区域");
        area.setEnabled(true);

        when(airportAreaService.findByAreaCode(areaCode)).thenReturn(Optional.of(area));
        when(spatialRuleService.findActiveRulesForArea(eq(areaCode), eq(RuleCategory.ACCESS_CONTROL)))
                .thenReturn(Collections.emptyList());

        // Act
        GeofenceAccessCheckResult result = geofenceService.checkAreaAccess(unmannedVehicle, areaCode);

        // Assert
        assertNotNull(result);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.ALLOWED, result.getResultType());
        assertTrue(result.getAllowed());
        assertEquals(areaCode, result.getAreaCode());
        assertEquals("UV001", result.getVehicleId());
        assertEquals("允许进入", result.getMessage());
    }

    @Test
    void testCheckAreaAccess_NonUnmannedVehicle_ShouldSkip() {
        // Arrange
        String areaCode = "TEST_AREA_01";
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject aircraft = MovingObject.builder()
                .objectId("AC001")
                .objectType(MovingObjectType.AIRCRAFT)
                .objectName("测试航空器")
                .currentPosition(position)
                .build();

        // Act
        GeofenceAccessCheckResult result = geofenceService.checkAreaAccess(aircraft, areaCode);

        // Assert
        assertNotNull(result);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.SKIPPED, result.getResultType());
        assertNull(result.getAllowed());
        assertEquals("AC001", result.getVehicleId());
        assertEquals("非无人车，跳过电子围栏检测", result.getMessage());
        
        // Verify no service calls were made
        verifyNoInteractions(airportAreaService);
        verifyNoInteractions(spatialRuleService);
    }

    @Test
    void testCheckAreaAccess_AreaNotExists_ShouldDeny() {
        // Arrange
        String areaCode = "NONEXISTENT_AREA";
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject unmannedVehicle = MovingObject.builder()
                .objectId("UV001")
                .objectType(MovingObjectType.UNMANNED_VEHICLE)
                .objectName("测试无人车")
                .currentPosition(position)
                .build();

        when(airportAreaService.findByAreaCode(areaCode)).thenReturn(Optional.empty());

        // Act
        GeofenceAccessCheckResult result = geofenceService.checkAreaAccess(unmannedVehicle, areaCode);

        // Assert
        assertNotNull(result);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.DENIED, result.getResultType());
        assertFalse(result.getAllowed());
        assertEquals(areaCode, result.getAreaCode());
        assertEquals("UV001", result.getVehicleId());
        assertEquals("区域不存在", result.getReason());
    }

    @Test
    void testCheckAreaAccess_AreaDisabled_ShouldDeny() {
        // Arrange
        String areaCode = "DISABLED_AREA";
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject unmannedVehicle = MovingObject.builder()
                .objectId("UV001")
                .objectType(MovingObjectType.UNMANNED_VEHICLE)
                .objectName("测试无人车")
                .currentPosition(position)
                .build();

        AirportArea disabledArea = new AirportArea();
        disabledArea.setAreaId(areaCode);
        disabledArea.setName("禁用区域");
        disabledArea.setEnabled(false); // 区域被禁用

        when(airportAreaService.findByAreaCode(areaCode)).thenReturn(Optional.of(disabledArea));

        // Act
        GeofenceAccessCheckResult result = geofenceService.checkAreaAccess(unmannedVehicle, areaCode);

        // Assert
        assertNotNull(result);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.DENIED, result.getResultType());
        assertFalse(result.getAllowed());
        assertEquals(areaCode, result.getAreaCode());
        assertEquals("UV001", result.getVehicleId());
        assertEquals("区域已禁用", result.getReason());
    }

    @Test
    void testCheckCurrentPosition_UnmannedVehicle_WithAreas() {
        // Arrange
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject unmannedVehicle = MovingObject.builder()
                .objectId("UV001")
                .objectType(MovingObjectType.UNMANNED_VEHICLE)
                .objectName("测试无人车")
                .currentPosition(position)
                .currentSpeed(15.0)
                .build();

        AirportArea area1 = new AirportArea();
        area1.setAreaId("AREA_01");
        area1.setName("区域1");
        area1.setEnabled(true);

        when(airportAreaService.findAreasContainingPoint(position))
                .thenReturn(List.of(area1));
        when(airportAreaService.findByAreaCode("AREA_01")).thenReturn(Optional.of(area1));
        when(spatialRuleService.findActiveRulesForArea(eq("AREA_01"), eq(RuleCategory.ACCESS_CONTROL)))
                .thenReturn(Collections.emptyList());

        // Act
        List<GeofenceAccessCheckResult> results = geofenceService.checkCurrentPosition(unmannedVehicle);

        // Assert
        assertNotNull(results);
        assertEquals(1, results.size());
        
        GeofenceAccessCheckResult result = results.get(0);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.ALLOWED, result.getResultType());
        assertTrue(result.getAllowed());
        assertEquals("AREA_01", result.getAreaCode());
        assertEquals("UV001", result.getVehicleId());
    }

    @Test
    void testCheckCurrentPosition_NonUnmannedVehicle_ShouldSkip() {
        // Arrange
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject aircraft = MovingObject.builder()
                .objectId("AC001")
                .objectType(MovingObjectType.AIRCRAFT)
                .objectName("测试航空器")
                .currentPosition(position)
                .build();

        // Act
        List<GeofenceAccessCheckResult> results = geofenceService.checkCurrentPosition(aircraft);

        // Assert
        assertNotNull(results);
        assertEquals(1, results.size());
        
        GeofenceAccessCheckResult result = results.get(0);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.SKIPPED, result.getResultType());
        assertNull(result.getAllowed());
        assertEquals("AC001", result.getVehicleId());
        
        // Verify no service calls were made
        verifyNoInteractions(airportAreaService);
        verifyNoInteractions(spatialRuleService);
    }

    @Test
    void testCheckCurrentPosition_NullPosition_ShouldReturnEmpty() {
        // Arrange
        MovingObject unmannedVehicle = MovingObject.builder()
                .objectId("UV001")
                .objectType(MovingObjectType.UNMANNED_VEHICLE)
                .objectName("测试无人车")
                .currentPosition(null) // 位置为空
                .build();

        // Act
        List<GeofenceAccessCheckResult> results = geofenceService.checkCurrentPosition(unmannedVehicle);

        // Assert
        assertNotNull(results);
        assertTrue(results.isEmpty());
        
        // Verify no service calls were made
        verifyNoInteractions(airportAreaService);
        verifyNoInteractions(spatialRuleService);
    }
    
    @Test
    void testCheckCurrentPosition_WithDeniedAccess_ShouldPublishWebSocketEvent() {
        // Arrange
        Point position = geometryFactory.createPoint(new Coordinate(120.0, 36.0));
        
        MovingObject unmannedVehicle = MovingObject.builder()
                .objectId("UV001")
                .objectType(MovingObjectType.UNMANNED_VEHICLE)
                .objectName("测试无人车")
                .currentPosition(position)
                .currentSpeed(15.0)
                .build();

        AirportArea area1 = new AirportArea();
        area1.setAreaId("RESTRICTED_AREA");
        area1.setName("限制区域");
        area1.setEnabled(false); // 禁用的区域，会被拒绝

        when(airportAreaService.findAreasContainingPoint(position))
                .thenReturn(List.of(area1));
        when(airportAreaService.findByAreaCode("RESTRICTED_AREA")).thenReturn(Optional.of(area1));

        // Act
        List<GeofenceAccessCheckResult> results = geofenceService.checkCurrentPosition(unmannedVehicle);

        // Assert
        assertNotNull(results);
        assertEquals(1, results.size());
        
        GeofenceAccessCheckResult result = results.get(0);
        assertEquals(GeofenceAccessCheckResult.AccessResultType.DENIED, result.getResultType());
        assertFalse(result.getAllowed());
        assertEquals("RESTRICTED_AREA", result.getAreaCode());
        assertEquals("UV001", result.getVehicleId());
        assertEquals("区域已禁用", result.getReason());
        
        // 验证WebSocket事件发布
        ArgumentCaptor<GeofenceAlertWebSocketEvent> eventCaptor = ArgumentCaptor.forClass(GeofenceAlertWebSocketEvent.class);
        verify(eventPublisher).publishEvent(eventCaptor.capture());
        
        GeofenceAlertWebSocketEvent publishedEvent = eventCaptor.getValue();
        assertNotNull(publishedEvent);
        assertEquals("geofence_alert", publishedEvent.getEventType());
        assertNotNull(publishedEvent.getPayload());
    }
}