"""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 DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import issue_registry as ir
from homeassistant.setup import async_setup_component
from homeassistant.util import slugify

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,
        )


async def test_import_from_yaml_success(
    hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
    """Test import."""

    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 async_setup_component(
            hass,
            "camera",
            {
                "camera": {
                    "name": "config_test",
                    "platform": "local_file",
                    "file_path": "mock.file",
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.config_entries.async_has_entries(DOMAIN)
    state = hass.states.get("camera.config_test")
    assert state.attributes.get("file_path") == "mock.file"

    issue = issue_registry.async_get_issue(
        HOMEASSISTANT_DOMAIN, f"deprecated_yaml_{DOMAIN}"
    )
    assert issue
    assert issue.translation_key == "deprecated_yaml"


async def test_import_from_yaml_fails(
    hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
    """Test import fails due to not accessible file."""

    with (
        patch("os.path.isfile", Mock(return_value=True)),
        patch("os.access", Mock(return_value=False)),
        patch(
            "homeassistant.components.local_file.camera.mimetypes.guess_type",
            Mock(return_value=(None, None)),
        ),
    ):
        await async_setup_component(
            hass,
            "camera",
            {
                "camera": {
                    "name": "config_test",
                    "platform": "local_file",
                    "file_path": "mock.file",
                }
            },
        )
        await hass.async_block_till_done()

    assert not hass.config_entries.async_has_entries(DOMAIN)
    assert not hass.states.get("camera.config_test")

    issue = issue_registry.async_get_issue(
        DOMAIN, f"no_access_path_{slugify("mock.file")}"
    )
    assert issue
    assert issue.translation_key == "no_access_path"