"""Tests for the Xbox media source platform."""

from httpx import HTTPStatusError, RequestError, Response, TimeoutException
import pytest
from pythonxbox.api.provider.people.models import PeopleResponse
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.media_player import BrowseError
from homeassistant.components.media_source import (
    URI_SCHEME,
    Unresolvable,
    async_browse_media,
    async_resolve_media,
)
from homeassistant.components.xbox.const import DOMAIN
from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from tests.common import AsyncMock, MockConfigEntry, async_load_json_object_fixture
from tests.typing import MagicMock


@pytest.fixture(autouse=True)
async def setup_media_source(hass: HomeAssistant) -> None:
    """Setup media source component."""

    await async_setup_component(hass, "media_source", {})


@pytest.mark.usefixtures("xbox_live_client")
@pytest.mark.freeze_time("2025-11-06T17:12:27")
async def test_browse_media(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    snapshot: SnapshotAssertion,
) -> None:
    """Test browsing media."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    assert (
        await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}")
    ).as_dict() == snapshot(name="games_view")

    assert (
        await async_browse_media(
            hass, f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135"
        )
    ).as_dict() == snapshot(name="category_view")

    assert (
        await async_browse_media(
            hass, f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/gameclips"
        )
    ).as_dict() == snapshot(name="gameclips_view")

    assert (
        await async_browse_media(
            hass, f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/screenshots"
        )
    ).as_dict() == snapshot(name="screenshots_view")

    assert (
        await async_browse_media(
            hass, f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/game_media"
        )
    ).as_dict() == snapshot(name="game_media_view")

    assert (
        await async_browse_media(
            hass, f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/community_gameclips"
        )
    ).as_dict() == snapshot(name="community_gameclips_view")

    assert (
        await async_browse_media(
            hass,
            f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/community_screenshots",
        )
    ).as_dict() == snapshot(name="community_screenshots_view")


async def test_browse_media_accounts(
    hass: HomeAssistant,
    xbox_live_client: AsyncMock,
    snapshot: SnapshotAssertion,
) -> None:
    """Test browsing media we get account view if more than 1 account is configured."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="GSR Ae",
        data={
            "auth_implementation": "cloud",
            "token": {
                "access_token": "1234567890",
                "expires_at": 1760697327.7298331,
                "expires_in": 3600,
                "refresh_token": "0987654321",
                "scope": "XboxLive.signin XboxLive.offline_access",
                "service": "xbox",
                "token_type": "bearer",
                "user_id": "AAAAAAAAAAAAAAAAAAAAA",
            },
        },
        unique_id="271958441785640",
        minor_version=3,
    )
    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    xbox_live_client.people.get_friend_by_xuid.return_value = PeopleResponse(
        **(await async_load_json_object_fixture(hass, "people_batch2.json", DOMAIN))  # type: ignore[reportArgumentType]
    )

    config_entry2 = MockConfigEntry(
        domain=DOMAIN,
        title="Iqnavs",
        data={
            "auth_implementation": "cloud",
            "token": {
                "access_token": "1234567890",
                "expires_at": 1760697327.7298331,
                "expires_in": 3600,
                "refresh_token": "0987654321",
                "scope": "XboxLive.signin XboxLive.offline_access",
                "service": "xbox",
                "token_type": "bearer",
                "user_id": "AAAAAAAAAAAAAAAAAAAAA",
            },
        },
        unique_id="277923030577271",
        minor_version=3,
    )
    config_entry2.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry2.entry_id)
    await hass.async_block_till_done()

    assert config_entry2.state is ConfigEntryState.LOADED

    assert len(hass.config_entries.async_loaded_entries(DOMAIN)) == 2

    assert (
        await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}")
    ).as_dict() == snapshot


@pytest.mark.parametrize(
    ("media_content_id", "provider", "method"),
    [
        ("", "titlehub", "get_title_history"),
        ("/271958441785640", "titlehub", "get_title_history"),
        ("/271958441785640/1297287135", "titlehub", "get_title_info"),
        (
            "/271958441785640/1297287135/gameclips",
            "gameclips",
            "get_recent_clips_by_xuid",
        ),
        (
            "/271958441785640/1297287135/screenshots",
            "screenshots",
            "get_recent_screenshots_by_xuid",
        ),
        (
            "/271958441785640/1297287135/game_media",
            "titlehub",
            "get_title_info",
        ),
        (
            "/271958441785640/1297287135/community_gameclips",
            "gameclips",
            "get_recent_community_clips_by_title_id",
        ),
        (
            "/271958441785640/1297287135/community_screenshots",
            "screenshots",
            "get_recent_community_screenshots_by_title_id",
        ),
    ],
)
@pytest.mark.parametrize(
    ("exception", "translation_key"),
    [
        (
            HTTPStatusError("", request=MagicMock(), response=Response(500)),
            "request_exception",
        ),
        (
            RequestError(""),
            "request_exception",
        ),
        (TimeoutException(""), "timeout_exception"),
    ],
)
async def test_browse_media_exceptions(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    xbox_live_client: AsyncMock,
    media_content_id: str,
    provider: str,
    method: str,
    exception: Exception,
    translation_key: str,
) -> None:
    """Test browsing media exceptions."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    provider = getattr(xbox_live_client, provider)
    getattr(provider, method).side_effect = exception

    with pytest.raises(BrowseError) as e:
        await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}{media_content_id}")
    assert e.value.translation_key == translation_key


@pytest.mark.usefixtures("xbox_live_client")
async def test_browse_media_not_configured_exception(
    hass: HomeAssistant,
) -> None:
    """Test browsing media integration not configured exception."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="Mock title",
        data={
            "auth_implementation": "cloud",
            "token": {
                "access_token": "1234567890",
                "expires_at": 1760697327.7298331,
                "expires_in": 3600,
                "refresh_token": "0987654321",
                "scope": "XboxLive.signin XboxLive.offline_access",
                "service": "xbox",
                "token_type": "bearer",
                "user_id": "AAAAAAAAAAAAAAAAAAAAA",
            },
        },
        unique_id="2533274838782903",
        disabled_by=ConfigEntryDisabler.USER,
        minor_version=3,
    )

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.NOT_LOADED

    with pytest.raises(BrowseError) as e:
        await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}")
    assert e.value.translation_key == "xbox_not_configured"


@pytest.mark.usefixtures("xbox_live_client")
async def test_browse_media_account_not_configured_exception(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
) -> None:
    """Test browsing media account not configured exception."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    with pytest.raises(BrowseError) as e:
        await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/2533274838782903")
    assert e.value.translation_key == "account_not_configured"


@pytest.mark.parametrize(
    ("media_content_id", "url", "mime_type"),
    [
        (
            "/271958441785640/1297287135/screenshots/41593644-be22-43d6-b224-c7bebe14076e",
            "https://screenshotscontent-d5001.xboxlive.com/00097bbbbbbbbb23-41593644-be22-43d6-b224-c7bebe14076e/Screenshot-Original.png?sv=2015-12-11&sr=b&si=DefaultAccess&sig=ALKo3DE2HXqBTlpdyynIrH6RPKIECOF7zwotH%2Bb30Ts%3D",
            "image/png",
        ),
        (
            "/271958441785640/1297287135/gameclips/f87cc6ac-c291-4998-9124-d8b36c059b6a",
            "https://gameclipscontent-d2015.xboxlive.com/asset-d5448dbd-f45e-46ab-ae4e-a2e205a70e7c/GameClip-Original.MP4?sv=2015-12-11&sr=b&si=DefaultAccess&sig=ArSoLvy9EnQeBthGW6%2FbasedHHk0Jb6iXjI3EMq8oh8%3D&__gda__=1522241341_69f67a7a3533626ae90b52845664dc0c",
            "video/mp4",
        ),
        (
            "/271958441785640/1297287135/game_media/0",
            "https://store-images.s-microsoft.com/image/apps.35725.65457035095819016.56f55216-1bb9-40aa-8796-068cf3075fc1.c4bf34f8-ad40-4af3-914e-a85e75a76bed",
            "image/png",
        ),
        (
            "/271958441785640/1297287135/community_screenshots/504a78e5-be24-4020-a245-77cb528e91ea",
            "https://screenshotscontent-d5002.media.xboxlive.com/xuid-2535422966774043-private/504a78e5-be24-4020-a245-77cb528e91ea.PNG?skoid=296fcea0-0bf0-4a22-abf7-16b3524eba1b&sktid=68cd85cc-e0b3-43c8-ba3c-67686dbf8a67&skt=2025-11-06T10%3A21%3A06Z&ske=2025-11-07T10%3A21%3A06Z&sks=b&skv=2025-05-05&sv=2025-05-05&st=2025-11-06T10%3A35%3A59Z&se=2125-11-06T10%3A50%3A59Z&sr=b&sp=r&sig=TqUUNeuAzHawaXBTFfSVuUzuXbGOMgrDu0Q2VBTFd5U%3D",
            "image/png",
        ),
        (
            "/271958441785640/1297287135/community_gameclips/6fa2731a-8b58-4aa6-848c-4bf15734358b",
            "https://gameclipscontent-d3021.media.xboxlive.com/xuid-2535458333395495-private/6fa2731a-8b58-4aa6-848c-4bf15734358b.MP4?skoid=2938738c-0e58-4f21-9b82-98081ade42e2&sktid=68cd85cc-e0b3-43c8-ba3c-67686dbf8a67&skt=2025-11-06T08%3A20%3A51Z&ske=2025-11-07T08%3A20%3A51Z&sks=b&skv=2025-05-05&sv=2025-05-05&st=2025-11-06T10%3A05%3A41Z&se=2125-11-06T10%3A20%3A41Z&sr=b&sp=r&sig=s%2FWDtmE2cnAwl9iJJFcch3knbRlkxkALoinHQwCnNP0%3D&__gda__=1762438393_eb8a56c3f482d00099045aa892a2aa05",
            "video/mp4",
        ),
    ],
    ids=[
        "screenshot",
        "gameclips",
        "game_media",
        "community_screenshots",
        "community_gameclips",
    ],
)
@pytest.mark.usefixtures("xbox_live_client")
async def test_resolve_media(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    media_content_id: str,
    url: str,
    mime_type: str,
) -> None:
    """Test resolve media."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    media = await async_resolve_media(
        hass,
        f"{URI_SCHEME}{DOMAIN}{media_content_id}",
        None,
    )
    assert media.url == url
    assert media.mime_type == mime_type


@pytest.mark.parametrize(
    ("media_content_id", "provider", "method"),
    [
        (
            "/271958441785640/1297287135/screenshots/41593644-be22-43d6-b224-c7bebe14076e",
            "screenshots",
            "get_recent_screenshots_by_xuid",
        ),
        (
            "/271958441785640/1297287135/gameclips/f87cc6ac-c291-4998-9124-d8b36c059b6a",
            "gameclips",
            "get_recent_clips_by_xuid",
        ),
        (
            "/271958441785640/1297287135/community_screenshots/504a78e5-be24-4020-a245-77cb528e91ea",
            "screenshots",
            "get_recent_community_screenshots_by_title_id",
        ),
        (
            "/271958441785640/1297287135/community_gameclips/6fa2731a-8b58-4aa6-848c-4bf15734358b",
            "gameclips",
            "get_recent_community_clips_by_title_id",
        ),
        (
            "/271958441785640/1297287135/game_media/0",
            "titlehub",
            "get_title_info",
        ),
    ],
)
@pytest.mark.parametrize(
    ("exception", "translation_key"),
    [
        (
            HTTPStatusError("", request=MagicMock(), response=Response(500)),
            "request_exception",
        ),
        (RequestError(""), "request_exception"),
        (TimeoutException(""), "timeout_exception"),
    ],
)
async def test_resolve_media_exceptions(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    xbox_live_client: AsyncMock,
    media_content_id: str,
    provider: str,
    method: str,
    exception: Exception,
    translation_key: str,
) -> None:
    """Test resolve media exceptions."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    provider = getattr(xbox_live_client, provider)
    getattr(provider, method).side_effect = exception

    with pytest.raises(Unresolvable) as e:
        await async_resolve_media(
            hass,
            f"{URI_SCHEME}{DOMAIN}{media_content_id}",
            None,
        )
    assert e.value.translation_key == translation_key


@pytest.mark.parametrize(("media_type"), ["screenshots", "gameclips", "game_media"])
@pytest.mark.usefixtures("xbox_live_client")
async def test_resolve_media_not_found_exceptions(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    media_type: str,
) -> None:
    """Test resolve media not found exceptions."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    with pytest.raises(Unresolvable) as e:
        await async_resolve_media(
            hass,
            f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/{media_type}/12345",
            None,
        )
    assert e.value.translation_key == "media_not_found"


@pytest.mark.usefixtures("xbox_live_client")
async def test_resolve_media_not_configured(
    hass: HomeAssistant,
) -> None:
    """Test resolve media integration not configured exception."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="Mock title",
        data={
            "auth_implementation": "cloud",
            "token": {
                "access_token": "1234567890",
                "expires_at": 1760697327.7298331,
                "expires_in": 3600,
                "refresh_token": "0987654321",
                "scope": "XboxLive.signin XboxLive.offline_access",
                "service": "xbox",
                "token_type": "bearer",
                "user_id": "AAAAAAAAAAAAAAAAAAAAA",
            },
        },
        unique_id="2533274838782903",
        disabled_by=ConfigEntryDisabler.USER,
    )

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.NOT_LOADED

    with pytest.raises(Unresolvable) as e:
        await async_resolve_media(
            hass,
            f"{URI_SCHEME}{DOMAIN}/2533274838782903",
            None,
        )
    assert e.value.translation_key == "xbox_not_configured"


@pytest.mark.usefixtures("xbox_live_client")
async def test_resolve_media_account_not_configured(
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
) -> None:
    """Test resolve media account not configured exception."""

    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.LOADED

    with pytest.raises(Unresolvable) as e:
        await async_resolve_media(
            hass,
            f"{URI_SCHEME}{DOMAIN}/2533274838782903",
            None,
        )
    assert e.value.translation_key == "account_not_configured"
