"""Tests for Fritz!Tools services."""

from unittest.mock import patch

from fritzconnection.core.exceptions import (
    FritzActionFailedError,
    FritzConnectionException,
    FritzServiceError,
)
import pytest
from voluptuous import MultipleInvalid

from homeassistant.components.fritz.const import DOMAIN
from homeassistant.components.fritz.services import (
    SERVICE_DIAL,
    SERVICE_SET_GUEST_WIFI_PW,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component

from .const import MOCK_SERIAL_NUMBER, MOCK_USER_DATA

from tests.common import MockConfigEntry


async def test_setup_services(hass: HomeAssistant) -> None:
    """Test setup of Fritz!Tools services."""
    assert await async_setup_component(hass, DOMAIN, {})
    await hass.async_block_till_done()

    services = hass.services.async_services_for_domain(DOMAIN)
    assert services
    assert SERVICE_SET_GUEST_WIFI_PW in services
    assert SERVICE_DIAL in services


async def test_service_set_guest_wifi_password(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
) -> None:
    """Test service set_guest_wifi_password."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device
    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_set_guest_password"
    ) as mock_async_trigger_set_guest_password:
        await hass.services.async_call(
            DOMAIN, SERVICE_SET_GUEST_WIFI_PW, {"device_id": device.id}
        )
        assert mock_async_trigger_set_guest_password.called


async def test_service_set_guest_wifi_password_unknown_parameter(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test service set_guest_wifi_password with unknown parameter."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_set_guest_password",
        side_effect=FritzServiceError("boom"),
    ) as mock_async_trigger_set_guest_password:
        await hass.services.async_call(
            DOMAIN, SERVICE_SET_GUEST_WIFI_PW, {"device_id": device.id}
        )
        assert mock_async_trigger_set_guest_password.called
        assert "HomeAssistantError: Action or parameter unknown" in caplog.text


async def test_service_set_guest_wifi_password_service_not_supported(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test service set_guest_wifi_password with connection error."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_set_guest_password",
        side_effect=FritzConnectionException("boom"),
    ) as mock_async_trigger_set_guest_password:
        await hass.services.async_call(
            DOMAIN, SERVICE_SET_GUEST_WIFI_PW, {"device_id": device.id}
        )
        assert mock_async_trigger_set_guest_password.called
        assert "HomeAssistantError: Action not supported" in caplog.text


async def test_service_set_guest_wifi_password_unloaded(
    hass: HomeAssistant,
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test service set_guest_wifi_password."""
    assert await async_setup_component(hass, DOMAIN, {})
    await hass.async_block_till_done()

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_set_guest_password"
    ) as mock_async_trigger_set_guest_password:
        await hass.services.async_call(
            DOMAIN, SERVICE_SET_GUEST_WIFI_PW, {"device_id": "12345678"}
        )
        assert not mock_async_trigger_set_guest_password.called
        assert (
            "ServiceValidationError: Failed to perform action"
            ' "set_guest_wifi_password".'
            " Config entry for target not found" in caplog.text
        )


async def test_service_dial(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test service dial."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device
    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial"
    ) as mock_async_trigger_dial:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_DIAL,
            {"device_id": device.id, "number": "1234567890", "max_ring_seconds": 10},
        )
        assert mock_async_trigger_dial.called
        assert mock_async_trigger_dial.call_args.kwargs == {"max_ring_seconds": 10}
        assert mock_async_trigger_dial.call_args.args == ("1234567890",)


async def test_service_dial_unknown_parameter(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test service dial with unknown parameters."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial",
        side_effect=FritzServiceError("boom"),
    ) as mock_async_trigger_dial:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_DIAL,
            {"device_id": device.id, "number": "1234567890", "max_ring_seconds": 10},
        )
        assert mock_async_trigger_dial.called
        assert "HomeAssistantError: Action or parameter unknown" in caplog.text


async def test_service_dial_wrong_parameter(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test service dial with unknown parameters."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial",
    ) as mock_async_trigger_dial:
        with pytest.raises(MultipleInvalid):
            await hass.services.async_call(
                DOMAIN,
                SERVICE_DIAL,
                {
                    "device_id": device.id,
                    "number": "1234567890",
                    "max_ring_seconds": "",
                },
            )
        assert not mock_async_trigger_dial.called
    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial",
    ) as mock_async_trigger_dial:
        with pytest.raises(MultipleInvalid):
            await hass.services.async_call(
                DOMAIN,
                SERVICE_DIAL,
                {
                    "device_id": device.id,
                    "number": "1234567890",
                    "max_ring_seconds": 0,
                },
            )
        assert not mock_async_trigger_dial.called


async def test_service_dial_service_not_supported(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test service dial with connection error."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial",
        side_effect=FritzConnectionException("boom"),
    ) as mock_async_trigger_dial:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_DIAL,
            {"device_id": device.id, "number": "1234567890", "max_ring_seconds": 10},
        )
        assert mock_async_trigger_dial.called
        assert "HomeAssistantError: Action not supported" in caplog.text


async def test_service_dial_failed(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    caplog: pytest.LogCaptureFixture,
    fc_class_mock,
    fh_class_mock,
    fs_class_mock,
) -> None:
    """Test dial service when the dial help is disabled."""
    assert await async_setup_component(hass, DOMAIN, {})
    entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
    entry.add_to_hass(hass)
    await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)}
    )
    assert device

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial",
        side_effect=FritzActionFailedError("boom"),
    ) as mock_async_trigger_dial:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_DIAL,
            {"device_id": device.id, "number": "1234567890", "max_ring_seconds": 10},
        )
        assert mock_async_trigger_dial.called
        assert (
            "HomeAssistantError: Failed to dial, check if the"
            " click to dial service of the FRITZ!Box is activated" in caplog.text
        )


async def test_service_dial_unloaded(
    hass: HomeAssistant,
    caplog: pytest.LogCaptureFixture,
    fs_class_mock,
) -> None:
    """Test service dial."""
    assert await async_setup_component(hass, DOMAIN, {})
    await hass.async_block_till_done()

    with patch(
        "homeassistant.components.fritz.coordinator.AvmWrapper.async_trigger_dial"
    ) as mock_async_trigger_dial:
        await hass.services.async_call(
            DOMAIN,
            SERVICE_DIAL,
            {"device_id": "12345678", "number": "1234567890", "max_ring_seconds": 10},
        )
        assert not mock_async_trigger_dial.called
        assert (
            "ServiceValidationError: Failed to perform action"
            f' "{SERVICE_DIAL}".'
            " Config entry for target not found" in caplog.text
        )
