"""Tests for various Plex services."""
from http import HTTPStatus
from unittest.mock import patch

from plexapi.exceptions import NotFound
import pytest

from homeassistant.components.media_player.const import MEDIA_TYPE_MUSIC
from homeassistant.components.plex.const import (
    CONF_SERVER,
    CONF_SERVER_IDENTIFIER,
    DOMAIN,
    PLEX_SERVER_CONFIG,
    SERVICE_REFRESH_LIBRARY,
    SERVICE_SCAN_CLIENTS,
)
from homeassistant.components.plex.services import play_on_sonos
from homeassistant.const import CONF_URL
from homeassistant.exceptions import HomeAssistantError

from .const import DEFAULT_OPTIONS, SECONDARY_DATA

from tests.common import MockConfigEntry


async def test_refresh_library(
    hass,
    mock_plex_server,
    setup_plex_server,
    requests_mock,
    empty_payload,
    plex_server_accounts,
    plex_server_base,
):
    """Test refresh_library service call."""
    url = mock_plex_server.url_in_use
    refresh = requests_mock.get(
        f"{url}/library/sections/1/refresh", status_code=HTTPStatus.OK
    )

    # Test with non-existent server
    with pytest.raises(HomeAssistantError):
        assert await hass.services.async_call(
            DOMAIN,
            SERVICE_REFRESH_LIBRARY,
            {"server_name": "Not a Server", "library_name": "Movies"},
            True,
        )
    assert not refresh.called

    # Test with non-existent library
    assert await hass.services.async_call(
        DOMAIN,
        SERVICE_REFRESH_LIBRARY,
        {"library_name": "Not a Library"},
        True,
    )
    assert not refresh.called

    # Test with valid library
    assert await hass.services.async_call(
        DOMAIN,
        SERVICE_REFRESH_LIBRARY,
        {"library_name": "Movies"},
        True,
    )
    assert refresh.call_count == 1

    # Add a second configured server
    secondary_url = SECONDARY_DATA[PLEX_SERVER_CONFIG][CONF_URL]
    secondary_name = SECONDARY_DATA[CONF_SERVER]
    secondary_id = SECONDARY_DATA[CONF_SERVER_IDENTIFIER]
    requests_mock.get(
        secondary_url,
        text=plex_server_base.format(
            name=secondary_name, machine_identifier=secondary_id
        ),
    )
    requests_mock.get(f"{secondary_url}/accounts", text=plex_server_accounts)
    requests_mock.get(f"{secondary_url}/clients", text=empty_payload)
    requests_mock.get(f"{secondary_url}/status/sessions", text=empty_payload)

    entry_2 = MockConfigEntry(
        domain=DOMAIN,
        data=SECONDARY_DATA,
        options=DEFAULT_OPTIONS,
        unique_id=SECONDARY_DATA["server_id"],
    )

    await setup_plex_server(config_entry=entry_2)

    # Test multiple servers available but none specified
    with pytest.raises(HomeAssistantError) as excinfo:
        assert await hass.services.async_call(
            DOMAIN,
            SERVICE_REFRESH_LIBRARY,
            {"library_name": "Movies"},
            True,
        )
    assert "Multiple Plex servers configured" in str(excinfo.value)
    assert refresh.call_count == 1


async def test_scan_clients(hass, mock_plex_server):
    """Test scan_for_clients service call."""
    assert await hass.services.async_call(
        DOMAIN,
        SERVICE_SCAN_CLIENTS,
        blocking=True,
    )


async def test_sonos_play_media(
    hass,
    entry,
    setup_plex_server,
    requests_mock,
    empty_payload,
    playqueue_1234,
    playqueue_created,
    plextv_account,
    sonos_resources,
):
    """Test playback from a Sonos media_player.play_media call."""
    media_content_id = (
        '{"library_name": "Music", "artist_name": "Artist", "album_name": "Album"}'
    )
    sonos_speaker_name = "Zone A"

    requests_mock.get("https://plex.tv/users/account", text=plextv_account)
    requests_mock.post("/playqueues", text=playqueue_created)
    playback_mock = requests_mock.get(
        "/player/playback/playMedia", status_code=HTTPStatus.OK
    )

    # Test with no Plex integration available
    with pytest.raises(HomeAssistantError) as excinfo:
        play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
    assert "Plex integration not configured" in str(excinfo.value)

    with patch(
        "homeassistant.components.plex.PlexServer.connect", side_effect=NotFound
    ):
        # Initialize Plex integration without setting up a server
        with pytest.raises(AssertionError):
            await setup_plex_server()

        # Test with no Plex servers available
        with pytest.raises(HomeAssistantError) as excinfo:
            play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
        assert "No Plex servers available" in str(excinfo.value)

    # Complete setup of a Plex server
    await hass.config_entries.async_unload(entry.entry_id)
    mock_plex_server = await setup_plex_server()

    # Test with unlinked Plex/Sonos accounts
    requests_mock.get("https://sonos.plex.tv/resources", status_code=403)
    with pytest.raises(HomeAssistantError) as excinfo:
        play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
    assert "Sonos speakers not linked to Plex account" in str(excinfo.value)
    assert playback_mock.call_count == 0

    # Test with no speakers available
    requests_mock.get("https://sonos.plex.tv/resources", text=empty_payload)
    with pytest.raises(HomeAssistantError) as excinfo:
        play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
    assert f"Sonos speaker '{sonos_speaker_name}' is not associated with" in str(
        excinfo.value
    )
    assert playback_mock.call_count == 0

    # Test with speakers available
    requests_mock.get("https://sonos.plex.tv/resources", text=sonos_resources)
    with patch.object(mock_plex_server.account, "_sonos_cache_timestamp", 0):
        play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
    assert playback_mock.call_count == 1

    # Test with speakers available and media key payload
    play_on_sonos(hass, MEDIA_TYPE_MUSIC, "100", sonos_speaker_name)
    assert playback_mock.call_count == 2

    # Test with speakers available and Plex server specified
    content_id_with_server = '{"plex_server": "Plex Server 1", "library_name": "Music", "artist_name": "Artist", "album_name": "Album"}'
    play_on_sonos(hass, MEDIA_TYPE_MUSIC, content_id_with_server, sonos_speaker_name)
    assert playback_mock.call_count == 3

    # Test with speakers available but media not found
    content_id_bad_media = '{"library_name": "Music", "artist_name": "Not an Artist"}'
    with patch("plexapi.library.LibrarySection.search", return_value=None):
        with pytest.raises(HomeAssistantError) as excinfo:
            play_on_sonos(
                hass, MEDIA_TYPE_MUSIC, content_id_bad_media, sonos_speaker_name
            )
        assert "Plex media not found" in str(excinfo.value)
        assert playback_mock.call_count == 3

    # Test with speakers available and playqueue
    requests_mock.get("https://1.2.3.4:32400/playQueues/1234", text=playqueue_1234)
    content_id_with_playqueue = '{"playqueue_id": 1234}'
    play_on_sonos(hass, MEDIA_TYPE_MUSIC, content_id_with_playqueue, sonos_speaker_name)
    assert playback_mock.call_count == 4

    # Test with speakers available and invalid playqueue
    requests_mock.get(
        "https://1.2.3.4:32400/playQueues/1235", status_code=HTTPStatus.NOT_FOUND
    )
    content_id_with_playqueue = '{"playqueue_id": 1235}'
    with pytest.raises(HomeAssistantError) as excinfo:
        play_on_sonos(
            hass, MEDIA_TYPE_MUSIC, content_id_with_playqueue, sonos_speaker_name
        )
    assert "PlayQueue '1235' could not be found" in str(excinfo.value)
    assert playback_mock.call_count == 4