"""Tests for Bosch Alarm component."""

import asyncio
from collections.abc import AsyncGenerator
import datetime as dt
from unittest.mock import AsyncMock, patch

import pytest
import voluptuous as vol

from homeassistant.components.bosch_alarm.const import (
    ATTR_DATETIME,
    DOMAIN,
    SERVICE_SET_DATE_TIME,
)
from homeassistant.const import ATTR_CONFIG_ENTRY_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util

from . import setup_integration

from tests.common import MockConfigEntry


@pytest.fixture(autouse=True)
async def platforms() -> AsyncGenerator[None]:
    """Return the platforms to be loaded for this test."""
    with patch("homeassistant.components.bosch_alarm.PLATFORMS", []):
        yield


async def test_set_date_time_service(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls succeed if the service call is valid."""
    await setup_integration(hass, mock_config_entry)
    await hass.services.async_call(
        DOMAIN,
        SERVICE_SET_DATE_TIME,
        {
            ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
            ATTR_DATETIME: dt_util.now(),
        },
        blocking=True,
    )
    mock_panel.set_panel_date.assert_called_once()


async def test_set_date_time_service_fails_bad_entity(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls fail if done for an incorrect entity."""
    await setup_integration(hass, mock_config_entry)
    with pytest.raises(ServiceValidationError) as err:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_SET_DATE_TIME,
            {
                ATTR_CONFIG_ENTRY_ID: "bad-config_id",
                ATTR_DATETIME: dt_util.now(),
            },
            blocking=True,
        )
    assert err.value.translation_key == "service_config_entry_not_found"
    assert err.value.translation_placeholders["entry_id"] == "bad-config_id"


async def test_set_date_time_service_fails_bad_params(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls fail if done with incorrect params."""
    await setup_integration(hass, mock_config_entry)
    with pytest.raises(
        vol.MultipleInvalid,
        match=r"Invalid datetime specified:  for dictionary value @ data\['datetime'\]",
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_SET_DATE_TIME,
            {
                ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
                ATTR_DATETIME: "",
            },
            blocking=True,
        )


async def test_set_date_time_service_fails_bad_year_before(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls fail if the panel fails the service call."""
    await setup_integration(hass, mock_config_entry)
    with pytest.raises(
        vol.MultipleInvalid,
        match=r"datetime must be before 2038 for dictionary value @ data\['datetime'\]",
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_SET_DATE_TIME,
            {
                ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
                ATTR_DATETIME: dt.datetime(2038, 1, 1),
            },
            blocking=True,
        )


async def test_set_date_time_service_fails_bad_year_after(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls fail if the panel fails the service call."""
    await setup_integration(hass, mock_config_entry)
    mock_panel.set_panel_date.side_effect = ValueError()
    with pytest.raises(
        vol.MultipleInvalid,
        match=r"datetime must be after 2009 for dictionary value @ data\['datetime'\]",
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_SET_DATE_TIME,
            {
                ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
                ATTR_DATETIME: dt.datetime(2009, 1, 1),
            },
            blocking=True,
        )


async def test_set_date_time_service_fails_connection_error(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls fail if the panel fails the service call."""
    await setup_integration(hass, mock_config_entry)
    mock_panel.set_panel_date.side_effect = asyncio.InvalidStateError()
    with pytest.raises(
        HomeAssistantError,
        match=f'Could not connect to "{mock_config_entry.title}"',
    ):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_SET_DATE_TIME,
            {
                ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
                ATTR_DATETIME: dt_util.now(),
            },
            blocking=True,
        )


async def test_set_date_time_service_fails_unloaded(
    hass: HomeAssistant,
    mock_panel: AsyncMock,
    area: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that the service calls fail if the config entry is unloaded."""
    await async_setup_component(hass, DOMAIN, {})
    mock_config_entry.add_to_hass(hass)
    with pytest.raises(ServiceValidationError) as err:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_SET_DATE_TIME,
            {
                ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
                ATTR_DATETIME: dt_util.now(),
            },
            blocking=True,
        )
    assert err.value.translation_key == "service_config_entry_not_loaded"
    assert err.value.translation_placeholders["entry_title"] == "Mock Title"
