"""Test the UniFi Protect media_player platform."""

from unittest.mock import AsyncMock, Mock, patch

import pytest
from uiprotect.data import AiPort, Camera
from uiprotect.exceptions import StreamError

from homeassistant.components.media_player import (
    ATTR_MEDIA_CONTENT_TYPE,
    ATTR_MEDIA_VOLUME_LEVEL,
)
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
from homeassistant.const import (
    ATTR_ATTRIBUTION,
    ATTR_ENTITY_ID,
    ATTR_SUPPORTED_FEATURES,
    STATE_IDLE,
    STATE_PLAYING,
    Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er

from . import patch_ufp_method
from .utils import (
    MockUFPFixture,
    adopt_devices,
    assert_entity_counts,
    init_entry,
    remove_entities,
)


async def test_media_player_camera_remove(
    hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
) -> None:
    """Test removing and re-adding a light device."""

    await init_entry(hass, ufp, [doorbell])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
    await remove_entities(hass, ufp, [doorbell])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 0, 0)
    await adopt_devices(hass, ufp, [doorbell])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)


async def test_media_player_setup(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity setup."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    unique_id = f"{doorbell.mac}_speaker"
    entity_id = "media_player.test_camera_speaker"

    entity = entity_registry.async_get(entity_id)
    assert entity
    assert entity.unique_id == unique_id

    expected_volume = float(doorbell.speaker_settings.speaker_volume / 100)

    state = hass.states.get(entity_id)
    assert state
    assert state.state == STATE_IDLE
    assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
    assert state.attributes[ATTR_SUPPORTED_FEATURES] == 136708
    assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == "music"
    assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == expected_volume


async def test_media_player_update(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity update."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    new_camera = doorbell.model_copy()
    new_camera.talkback_stream = Mock()
    new_camera.talkback_stream.is_running = True

    mock_msg = Mock()
    mock_msg.changed_data = {}
    mock_msg.new_obj = new_camera

    ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
    ufp.ws_msg(mock_msg)
    await hass.async_block_till_done()

    state = hass.states.get("media_player.test_camera_speaker")
    assert state
    assert state.state == STATE_PLAYING


async def test_media_player_set_volume(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity test set_volume_level."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    with patch_ufp_method(
        doorbell, "set_speaker_volume", new_callable=AsyncMock
    ) as mock_method:
        await hass.services.async_call(
            "media_player",
            "volume_set",
            {ATTR_ENTITY_ID: "media_player.test_camera_speaker", "volume_level": 0.5},
            blocking=True,
        )

        mock_method.assert_called_once_with(50)


async def test_media_player_stop(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity test media_stop."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    new_camera = doorbell.model_copy()
    new_camera.talkback_stream = AsyncMock()
    new_camera.talkback_stream.is_running = True

    mock_msg = Mock()
    mock_msg.changed_data = {}
    mock_msg.new_obj = new_camera

    ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
    ufp.ws_msg(mock_msg)
    await hass.async_block_till_done()

    await hass.services.async_call(
        "media_player",
        "media_stop",
        {ATTR_ENTITY_ID: "media_player.test_camera_speaker"},
        blocking=True,
    )

    new_camera.talkback_stream.stop.assert_called_once()


async def test_media_player_play(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity test play_media."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    with (
        patch_ufp_method(doorbell, "stop_audio", new_callable=AsyncMock),
        patch_ufp_method(doorbell, "play_audio", new_callable=AsyncMock) as mock_play,
        patch_ufp_method(
            doorbell, "wait_until_audio_completes", new_callable=AsyncMock
        ) as mock_wait,
    ):
        await hass.services.async_call(
            "media_player",
            "play_media",
            {
                ATTR_ENTITY_ID: "media_player.test_camera_speaker",
                "media_content_id": "http://example.com/test.mp3",
                "media_content_type": "music",
            },
            blocking=True,
        )

        mock_play.assert_called_once_with("http://example.com/test.mp3", blocking=False)
        mock_wait.assert_called_once()


async def test_media_player_play_media_source(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity test play_media."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    with (
        patch_ufp_method(doorbell, "stop_audio", new_callable=AsyncMock),
        patch_ufp_method(doorbell, "play_audio", new_callable=AsyncMock) as mock_play,
        patch_ufp_method(
            doorbell, "wait_until_audio_completes", new_callable=AsyncMock
        ) as mock_wait,
        patch(
            "homeassistant.components.media_source.async_resolve_media",
            return_value=Mock(url="http://example.com/test.mp3"),
        ),
    ):
        await hass.services.async_call(
            "media_player",
            "play_media",
            {
                ATTR_ENTITY_ID: "media_player.test_camera_speaker",
                "media_content_id": "media-source://some_source/some_id",
                "media_content_type": "audio/mpeg",
            },
            blocking=True,
        )

        mock_play.assert_called_once_with("http://example.com/test.mp3", blocking=False)
        mock_wait.assert_called_once()


async def test_media_player_play_invalid(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity test play_media, not music."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    with patch_ufp_method(
        doorbell, "play_audio", new_callable=AsyncMock
    ) as mock_method:
        with pytest.raises(HomeAssistantError):
            await hass.services.async_call(
                "media_player",
                "play_media",
                {
                    ATTR_ENTITY_ID: "media_player.test_camera_speaker",
                    "media_content_id": "/test.png",
                    "media_content_type": "image",
                },
                blocking=True,
            )

        assert not mock_method.called


async def test_media_player_play_error(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    doorbell: Camera,
    unadopted_camera: Camera,
) -> None:
    """Test media_player entity test play_media, not music."""

    await init_entry(hass, ufp, [doorbell, unadopted_camera])
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)

    with (
        patch_ufp_method(
            doorbell, "play_audio", new_callable=AsyncMock, side_effect=StreamError
        ) as mock_play,
        patch_ufp_method(
            doorbell, "wait_until_audio_completes", new_callable=AsyncMock
        ) as mock_wait,
    ):
        with pytest.raises(HomeAssistantError):
            await hass.services.async_call(
                "media_player",
                "play_media",
                {
                    ATTR_ENTITY_ID: "media_player.test_camera_speaker",
                    "media_content_id": "/test.mp3",
                    "media_content_type": "music",
                },
                blocking=True,
            )

        assert mock_play.called
        assert not mock_wait.called


async def test_aiport_no_media_player_entities(
    hass: HomeAssistant,
    ufp: MockUFPFixture,
    aiport: AiPort,
) -> None:
    """Test that AI Port devices do not create camera-specific media player entities."""
    await init_entry(hass, ufp, [aiport])

    # AI Port should not create any media player entities (speaker)
    assert_entity_counts(hass, Platform.MEDIA_PLAYER, 0, 0)
