"""Test the Immich services."""

import re
from unittest.mock import Mock, patch

from aioimmich.exceptions import ImmichError, ImmichNotFoundError
import pytest

from homeassistant.components.immich.const import DOMAIN
from homeassistant.components.immich.services import SERVICE_UPLOAD_FILE
from homeassistant.components.media_source import PlayMedia
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import entity_registry as er

from . import setup_integration

from tests.common import MockConfigEntry


async def test_setup_services(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test setup of immich services."""
    await setup_integration(hass, mock_config_entry)

    services = hass.services.async_services_for_domain(DOMAIN)
    assert services
    assert SERVICE_UPLOAD_FILE in services


async def test_upload_file(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
    mock_media_source: Mock,
) -> None:
    """Test upload_file service."""
    await setup_integration(hass, mock_config_entry)
    await hass.services.async_call(
        DOMAIN,
        SERVICE_UPLOAD_FILE,
        {
            "config_entry_id": mock_config_entry.entry_id,
            "file": {
                "media_content_id": "media-source://media_source/local/screenshot.jpg",
                "media_content_type": "image/jpeg",
            },
        },
        blocking=True,
    )

    mock_immich.assets.async_upload_asset.assert_called_with("/media/screenshot.jpg")
    mock_immich.albums.async_get_album_info.assert_not_called()
    mock_immich.albums.async_add_assets_to_album.assert_not_called()


async def test_upload_file_to_album(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
    mock_media_source: Mock,
) -> None:
    """Test upload_file service with target album_id."""
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        DOMAIN,
        SERVICE_UPLOAD_FILE,
        {
            "config_entry_id": mock_config_entry.entry_id,
            "file": {
                "media_content_id": "media-source://media_source/local/screenshot.jpg",
                "media_content_type": "image/jpeg",
            },
            "album_id": "721e1a4b-aa12-441e-8d3b-5ac7ab283bb6",
        },
        blocking=True,
    )

    mock_immich.assets.async_upload_asset.assert_called_with("/media/screenshot.jpg")
    mock_immich.albums.async_get_album_info.assert_called_with(
        "721e1a4b-aa12-441e-8d3b-5ac7ab283bb6"
    )
    mock_immich.albums.async_add_assets_to_album.assert_called_with(
        "721e1a4b-aa12-441e-8d3b-5ac7ab283bb6", ["abcdef-0123456789"]
    )


async def test_upload_file_config_entry_not_found(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test upload_file service raising config_entry_not_found."""
    await setup_integration(hass, mock_config_entry)

    with pytest.raises(ServiceValidationError) as err:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPLOAD_FILE,
            {
                "config_entry_id": "unknown_entry",
                "file": {
                    "media_content_id": "media-source://media_source/local/screenshot.jpg",
                    "media_content_type": "image/jpeg",
                },
            },
            blocking=True,
        )
    assert err.value.translation_key == "service_config_entry_not_found"


async def test_upload_file_config_entry_not_loaded(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test upload_file service raising config_entry_not_loaded."""
    mock_config_entry.disabled_by = er.RegistryEntryDisabler.USER
    await setup_integration(hass, mock_config_entry)

    with pytest.raises(ServiceValidationError) as err:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPLOAD_FILE,
            {
                "config_entry_id": mock_config_entry.entry_id,
                "file": {
                    "media_content_id": "media-source://media_source/local/screenshot.jpg",
                    "media_content_type": "image/jpeg",
                },
            },
            blocking=True,
        )
    assert err.value.translation_key == "service_config_entry_not_loaded"


async def test_upload_file_only_local_media_supported(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
    mock_media_source: Mock,
) -> None:
    """Test upload_file service raising only_local_media_supported."""
    await setup_integration(hass, mock_config_entry)
    with (
        patch(
            "homeassistant.components.immich.services.async_resolve_media",
            return_value=PlayMedia(
                url="media-source://media_source/camera/some_entity_id",
                mime_type="image/jpeg",
                path=None,  # Simulate non-local media
            ),
        ),
        pytest.raises(
            ServiceValidationError,
            match="Only local media files are currently supported",
        ),
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPLOAD_FILE,
            {
                "config_entry_id": mock_config_entry.entry_id,
                "file": {
                    "media_content_id": "media-source://media_source/local/screenshot.jpg",
                    "media_content_type": "image/jpeg",
                },
            },
            blocking=True,
        )


async def test_upload_file_album_not_found(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
    mock_media_source: Mock,
) -> None:
    """Test upload_file service raising album_not_found."""
    await setup_integration(hass, mock_config_entry)

    mock_immich.albums.async_get_album_info.side_effect = ImmichNotFoundError(
        {
            "message": "Not found or no album.read access",
            "error": "Bad Request",
            "statusCode": 400,
            "correlationId": "nyzxjkno",
        }
    )

    with pytest.raises(
        ServiceValidationError,
        match="Album with ID `721e1a4b-aa12-441e-8d3b-5ac7ab283bb6` not found",
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPLOAD_FILE,
            {
                "config_entry_id": mock_config_entry.entry_id,
                "file": {
                    "media_content_id": "media-source://media_source/local/screenshot.jpg",
                    "media_content_type": "image/jpeg",
                },
                "album_id": "721e1a4b-aa12-441e-8d3b-5ac7ab283bb6",
            },
            blocking=True,
        )


@pytest.mark.parametrize(
    ("side_effect", "expected_err_message"),
    [
        (
            ImmichError(
                {
                    "message": "Boom! Upload failed",
                    "error": "Bad Request",
                    "statusCode": 400,
                    "correlationId": "nyzxjkno",
                }
            ),
            "Boom! Upload failed (error: 'Bad Request' code: '400' correlation_id: 'nyzxjkno')",
        ),
        (
            FileNotFoundError(2, "No such file or directory", "/media/screenshot.jpg"),
            "[Errno 2] No such file or directory: '/media/screenshot.jpg'",
        ),
    ],
)
async def test_upload_file_upload_failed(
    hass: HomeAssistant,
    side_effect: Exception,
    expected_err_message: str,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
    mock_media_source: Mock,
) -> None:
    """Test upload_file service raising upload_failed."""
    await setup_integration(hass, mock_config_entry)

    mock_immich.assets.async_upload_asset.side_effect = side_effect
    with pytest.raises(
        ServiceValidationError,
        match=re.escape(
            f"Upload of file `/media/screenshot.jpg` failed ({expected_err_message})"
        ),
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPLOAD_FILE,
            {
                "config_entry_id": mock_config_entry.entry_id,
                "file": {
                    "media_content_id": "media-source://media_source/local/screenshot.jpg",
                    "media_content_type": "image/jpeg",
                },
            },
            blocking=True,
        )


async def test_upload_file_to_album_upload_failed(
    hass: HomeAssistant,
    mock_immich: Mock,
    mock_config_entry: MockConfigEntry,
    mock_media_source: Mock,
) -> None:
    """Test upload_file service with target album_id raising upload_failed."""
    await setup_integration(hass, mock_config_entry)

    mock_immich.albums.async_add_assets_to_album.side_effect = ImmichError(
        {
            "message": "Boom! Add to album failed.",
            "error": "Bad Request",
            "statusCode": 400,
            "correlationId": "nyzxjkno",
        }
    )
    with pytest.raises(
        ServiceValidationError, match="Upload of file `/media/screenshot.jpg` failed"
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPLOAD_FILE,
            {
                "config_entry_id": mock_config_entry.entry_id,
                "file": {
                    "media_content_id": "media-source://media_source/local/screenshot.jpg",
                    "media_content_type": "image/jpeg",
                },
                "album_id": "721e1a4b-aa12-441e-8d3b-5ac7ab283bb6",
            },
            blocking=True,
        )
