"""Test camera WebRTC."""

import pytest

from homeassistant.components.camera import Camera
from homeassistant.components.camera.const import StreamType
from homeassistant.components.camera.helper import get_camera_from_entity_id
from homeassistant.components.camera.webrtc import (
    DATA_ICE_SERVERS,
    CameraWebRTCProvider,
    RTCIceServer,
    async_register_webrtc_provider,
    register_ice_server,
)
from homeassistant.components.websocket_api import TYPE_RESULT
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from tests.typing import WebSocketGenerator


@pytest.mark.usefixtures("mock_camera", "mock_stream", "mock_stream_source")
async def test_async_register_webrtc_provider(
    hass: HomeAssistant,
) -> None:
    """Test registering a WebRTC provider."""
    await async_setup_component(hass, "camera", {})

    camera = get_camera_from_entity_id(hass, "camera.demo_camera")
    assert camera.frontend_stream_type is StreamType.HLS

    stream_supported = True

    class TestProvider(CameraWebRTCProvider):
        """Test provider."""

        async def async_is_supported(self, stream_source: str) -> bool:
            """Determine if the provider supports the stream source."""
            nonlocal stream_supported
            return stream_supported

        async def async_handle_web_rtc_offer(
            self, camera: Camera, offer_sdp: str
        ) -> str | None:
            """Handle the WebRTC offer and return an answer."""
            return "answer"

    unregister = async_register_webrtc_provider(hass, TestProvider())
    await hass.async_block_till_done()

    assert camera.frontend_stream_type is StreamType.WEB_RTC

    # Mark stream as unsupported
    stream_supported = False
    # Manually refresh the provider
    await camera.async_refresh_providers()

    assert camera.frontend_stream_type is StreamType.HLS

    # Mark stream as unsupported
    stream_supported = True
    # Manually refresh the provider
    await camera.async_refresh_providers()
    assert camera.frontend_stream_type is StreamType.WEB_RTC

    unregister()
    await hass.async_block_till_done()

    assert camera.frontend_stream_type is StreamType.HLS


@pytest.mark.usefixtures("mock_camera", "mock_stream", "mock_stream_source")
async def test_async_register_webrtc_provider_twice(
    hass: HomeAssistant,
) -> None:
    """Test registering a WebRTC provider twice should raise."""
    await async_setup_component(hass, "camera", {})

    class TestProvider(CameraWebRTCProvider):
        """Test provider."""

        async def async_is_supported(self, stream_source: str) -> bool:
            """Determine if the provider supports the stream source."""
            return True

        async def async_handle_web_rtc_offer(
            self, camera: Camera, offer_sdp: str
        ) -> str | None:
            """Handle the WebRTC offer and return an answer."""
            return "answer"

    provider = TestProvider()
    async_register_webrtc_provider(hass, provider)
    await hass.async_block_till_done()

    with pytest.raises(ValueError, match="Provider already registered"):
        async_register_webrtc_provider(hass, provider)


async def test_async_register_webrtc_provider_camera_not_loaded(
    hass: HomeAssistant,
) -> None:
    """Test registering a WebRTC provider when camera is not loaded."""

    class TestProvider(CameraWebRTCProvider):
        """Test provider."""

        async def async_is_supported(self, stream_source: str) -> bool:
            """Determine if the provider supports the stream source."""
            return True

        async def async_handle_web_rtc_offer(
            self, camera: Camera, offer_sdp: str
        ) -> str | None:
            """Handle the WebRTC offer and return an answer."""
            return "answer"

    with pytest.raises(ValueError, match="Unexpected state, camera not loaded"):
        async_register_webrtc_provider(hass, TestProvider())


@pytest.mark.usefixtures("mock_camera", "mock_stream", "mock_stream_source")
async def test_async_register_ice_server(
    hass: HomeAssistant,
) -> None:
    """Test registering an ICE server."""
    await async_setup_component(hass, "camera", {})

    # Clear any existing ICE servers
    hass.data[DATA_ICE_SERVERS].clear()

    called = 0

    async def get_ice_server() -> RTCIceServer:
        nonlocal called
        called += 1
        return RTCIceServer(urls="stun:example.com")

    unregister = register_ice_server(hass, get_ice_server)
    assert not called

    camera = get_camera_from_entity_id(hass, "camera.demo_camera")
    config = await camera.async_get_webrtc_client_configuration()

    assert config.configuration.ice_servers == [RTCIceServer(urls="stun:example.com")]
    assert called == 1

    # register another ICE server
    called_2 = 0

    async def get_ice_server_2() -> RTCIceServer:
        nonlocal called_2
        called_2 += 1
        return RTCIceServer(
            urls=["stun:example2.com", "turn:example2.com"],
            username="user",
            credential="pass",
        )

    unregister_2 = register_ice_server(hass, get_ice_server_2)

    config = await camera.async_get_webrtc_client_configuration()
    assert config.configuration.ice_servers == [
        RTCIceServer(urls="stun:example.com"),
        RTCIceServer(
            urls=["stun:example2.com", "turn:example2.com"],
            username="user",
            credential="pass",
        ),
    ]
    assert called == 2
    assert called_2 == 1

    # unregister the first ICE server

    unregister()

    config = await camera.async_get_webrtc_client_configuration()
    assert config.configuration.ice_servers == [
        RTCIceServer(
            urls=["stun:example2.com", "turn:example2.com"],
            username="user",
            credential="pass",
        ),
    ]
    assert called == 2
    assert called_2 == 2

    # unregister the second ICE server
    unregister_2()

    config = await camera.async_get_webrtc_client_configuration()
    assert config.configuration.ice_servers == []


@pytest.mark.usefixtures("mock_camera_web_rtc")
async def test_ws_get_client_config(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
    """Test get WebRTC client config."""
    await async_setup_component(hass, "camera", {})

    client = await hass_ws_client(hass)
    await client.send_json_auto_id(
        {"type": "camera/webrtc/get_client_config", "entity_id": "camera.demo_camera"}
    )
    msg = await client.receive_json()

    # Assert WebSocket response
    assert msg["type"] == TYPE_RESULT
    assert msg["success"]
    assert msg["result"] == {
        "configuration": {"iceServers": [{"urls": "stun:stun.home-assistant.io:3478"}]}
    }


@pytest.mark.usefixtures("mock_camera_hls")
async def test_ws_get_client_config_no_rtc_camera(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
    """Test get WebRTC client config."""
    await async_setup_component(hass, "camera", {})

    client = await hass_ws_client(hass)
    await client.send_json_auto_id(
        {"type": "camera/webrtc/get_client_config", "entity_id": "camera.demo_camera"}
    )
    msg = await client.receive_json()

    # Assert WebSocket response
    assert msg["type"] == TYPE_RESULT
    assert not msg["success"]
    assert msg["error"] == {
        "code": "web_rtc_offer_failed",
        "message": "Camera does not support WebRTC, frontend_stream_type=hls",
    }