from __future__ import annotations

from unittest.mock import AsyncMock

import pytest

from src.edge_proxy.messages import EventLogMessage, NavStatusMessage
from src.executor.action_executor import NavState, NavigationContext
from src.server import OrchestratorServer, ServerConfig


@pytest.mark.asyncio
async def test_replayed_nav_failure_updates_executor_and_broadcasts_reasoning():
    server = OrchestratorServer(ServerConfig(enable_executor=True))
    assert server.executor is not None

    server.executor._nav_context = NavigationContext(
        request_id="nav_step1",
        destination="office_scene",
        speed="normal",
        status=NavState.NAVIGATING,
        progress=0.4,
    )
    server.broadcast_to_dashboard = AsyncMock()  # type: ignore[method-assign]
    server._generate_navigation_failure_reasoning = AsyncMock(  # type: ignore[method-assign]
        return_value="Localization confidence dropped; check rosbridge pose updates and relocalize."
    )

    msg = EventLogMessage(
        event_id="edge-evt-1",
        event_type="nav_status",
        request_id="nav_step1",
        status="failed",
        replay=True,
        payload={
            "destination": "office_scene",
            "progress": 0.8,
            "reason": "localization_unavailable",
            "error_code": "NAV_LOCALIZATION_LOST",
        },
    )

    await server._on_edge_event_log(msg)

    assert server.executor._nav_context.status == NavState.FAILED
    assert server.executor._nav_context.error == "localization_unavailable"

    message_types = [
        call.args[0]["type"] for call in server.broadcast_to_dashboard.await_args_list
    ]
    assert "edge_event" in message_types
    assert "reasoning" in message_types


@pytest.mark.asyncio
async def test_scan_area_capture_event_broadcasts_artifact():
    server = OrchestratorServer(ServerConfig(enable_executor=True))
    server.broadcast_to_dashboard = AsyncMock()  # type: ignore[method-assign]

    msg = EventLogMessage(
        event_id="edge-evt-2",
        event_type="scan_area_capture",
        request_id="nav_step2",
        status="saved",
        replay=True,
        payload={"path": "/tmp/edge_proxy_artifacts/demo.jpg", "size_bytes": 12345},
    )
    await server._on_edge_event_log(msg)

    message_types = [
        call.args[0]["type"] for call in server.broadcast_to_dashboard.await_args_list
    ]
    assert message_types.count("edge_event") == 1
    assert message_types.count("edge_artifact") == 1


@pytest.mark.asyncio
async def test_live_localization_failure_reasoning_is_deduplicated():
    server = OrchestratorServer(ServerConfig(enable_executor=True))
    server.broadcast_to_dashboard = AsyncMock()  # type: ignore[method-assign]
    server._generate_navigation_failure_reasoning = AsyncMock(  # type: ignore[method-assign]
        return_value="Localization failed because map/pose stream is stale."
    )

    msg = NavStatusMessage(
        request_id="nav_001",
        status="failed",
        destination="spill_scene",
        progress=0.2,
        reason="localization_unavailable",
        error_code="NAV_LOCALIZATION_LOST",
    )

    await server._on_nav_status(msg)
    await server._on_nav_status(msg)

    assert server._generate_navigation_failure_reasoning.await_count == 1
    reasoning_calls = [
        call for call in server.broadcast_to_dashboard.await_args_list
        if call.args and call.args[0].get("type") == "reasoning"
    ]
    assert len(reasoning_calls) == 1
