"""The tests for local file camera component."""

from http import HTTPStatus
from typing import Any
from unittest.mock import Mock, mock_open, patch

import pytest

from homeassistant.components.local_file.const import (
    DEFAULT_NAME,
    DOMAIN,
    SERVICE_UPDATE_FILE_PATH,
)
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import ATTR_ENTITY_ID, CONF_FILE_PATH
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError

from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator


async def test_loading_file(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    loaded_entry: MockConfigEntry,
) -> None:
    """Test that it loads image from disk."""

    client = await hass_client()

    m_open = mock_open(read_data=b"hello")
    with patch("homeassistant.components.local_file.camera.open", m_open, create=True):
        resp = await client.get("/api/camera_proxy/camera.local_file")

    assert resp.status == HTTPStatus.OK
    body = await resp.text()
    assert body == "hello"


async def test_file_not_readable_after_setup(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    caplog: pytest.LogCaptureFixture,
    loaded_entry: MockConfigEntry,
) -> None:
    """Test a warning is shown setup when file is not readable."""

    client = await hass_client()

    with patch(
        "homeassistant.components.local_file.camera.open", side_effect=FileNotFoundError
    ):
        resp = await client.get("/api/camera_proxy/camera.local_file")

    assert resp.status == HTTPStatus.INTERNAL_SERVER_ERROR
    assert "Could not read camera Local File image from file: mock.file" in caplog.text


@pytest.mark.parametrize(
    ("config", "url", "content_type"),
    [
        (
            {
                "name": "test_jpg",
                "file_path": "/path/to/image.jpg",
            },
            "/api/camera_proxy/camera.test_jpg",
            "image/jpeg",
        ),
        (
            {
                "name": "test_png",
                "file_path": "/path/to/image.png",
            },
            "/api/camera_proxy/camera.test_png",
            "image/png",
        ),
        (
            {
                "name": "test_svg",
                "file_path": "/path/to/image.svg",
            },
            "/api/camera_proxy/camera.test_svg",
            "image/svg+xml",
        ),
        (
            {
                "name": "test_no_ext",
                "file_path": "/path/to/image",
            },
            "/api/camera_proxy/camera.test_no_ext",
            "image/jpeg",
        ),
    ],
)
async def test_camera_content_type(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    config: dict[str, Any],
    url: str,
    content_type: str,
) -> None:
    """Test local_file camera content_type."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        source=SOURCE_USER,
        options=config,
        entry_id="1",
    )

    config_entry.add_to_hass(hass)
    with (
        patch("os.path.isfile", Mock(return_value=True)),
        patch("os.access", Mock(return_value=True)),
    ):
        await hass.config_entries.async_setup(config_entry.entry_id)
        await hass.async_block_till_done()

    client = await hass_client()

    image = "hello"
    m_open = mock_open(read_data=image.encode())
    with patch("homeassistant.components.local_file.camera.open", m_open, create=True):
        resp_1 = await client.get(url)

    assert resp_1.status == HTTPStatus.OK
    assert resp_1.content_type == content_type
    body = await resp_1.text()
    assert body == image


@pytest.mark.parametrize(
    "get_config",
    [
        {
            "name": DEFAULT_NAME,
            "file_path": "mock/path.jpg",
        }
    ],
)
async def test_update_file_path(
    hass: HomeAssistant, loaded_entry: MockConfigEntry
) -> None:
    """Test update_file_path service."""
    # Setup platform
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        source=SOURCE_USER,
        options={
            "name": "local_file_camera_2",
            "file_path": "mock/path_2.jpg",
        },
        entry_id="2",
    )

    config_entry.add_to_hass(hass)
    with (
        patch("os.path.isfile", Mock(return_value=True)),
        patch("os.access", Mock(return_value=True)),
        patch(
            "homeassistant.components.local_file.camera.mimetypes.guess_type",
            Mock(return_value=(None, None)),
        ),
    ):
        await hass.config_entries.async_setup(config_entry.entry_id)
        await hass.async_block_till_done()

    # Fetch state and check motion detection attribute
    state = hass.states.get("camera.local_file")
    assert state.attributes.get("friendly_name") == "Local File"
    assert state.attributes.get("file_path") == "mock/path.jpg"

    service_data = {"entity_id": "camera.local_file", "file_path": "new/path.jpg"}

    with (
        patch("os.path.isfile", Mock(return_value=True)),
        patch("os.access", Mock(return_value=True)),
        patch(
            "homeassistant.components.local_file.camera.mimetypes.guess_type",
            Mock(return_value=(None, None)),
        ),
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPDATE_FILE_PATH,
            service_data,
            blocking=True,
        )

    state = hass.states.get("camera.local_file")
    assert state.attributes.get("file_path") == "new/path.jpg"

    # Check that local_file_camera_2 file_path is still as configured
    state = hass.states.get("camera.local_file_camera_2")
    assert state.attributes.get("file_path") == "mock/path_2.jpg"

    # Assert it fails if file is not readable
    service_data = {
        ATTR_ENTITY_ID: "camera.local_file",
        CONF_FILE_PATH: "new/path2.jpg",
    }
    with pytest.raises(
        ServiceValidationError, match="Path new/path2.jpg is not accessible"
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_UPDATE_FILE_PATH,
            service_data,
            blocking=True,
        )
