"""Tests for the Portainer switch platform."""

from unittest.mock import AsyncMock, patch

from pyportainer.exceptions import (
    PortainerAuthenticationError,
    PortainerConnectionError,
    PortainerTimeoutError,
)
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.switch import (
    DOMAIN as SWITCH_DOMAIN,
    SERVICE_TURN_OFF,
    SERVICE_TURN_ON,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er

from . import setup_integration

from tests.common import MockConfigEntry, snapshot_platform


@pytest.mark.usefixtures("mock_portainer_client")
async def test_all_switch_entities_snapshot(
    hass: HomeAssistant,
    snapshot: SnapshotAssertion,
    mock_config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Snapshot test for all Portainer switch entities."""
    with patch(
        "homeassistant.components.portainer._PLATFORMS",
        [Platform.SWITCH],
    ):
        await setup_integration(hass, mock_config_entry)
        await snapshot_platform(
            hass, entity_registry, snapshot, mock_config_entry.entry_id
        )


@pytest.mark.parametrize(
    ("entity_id", "turn_on_method", "turn_off_method", "expected_args"),
    [
        (
            "switch.practical_morse_container",
            "start_container",
            "stop_container",
            (1, "ee20facfb3b3ed4cd362c1e88fc89a53908ad05fb3a4103bca3f9b28292d14bf"),
        ),
        (
            "switch.webstack_stack",
            "start_stack",
            "stop_stack",
            (1, 1),
        ),
    ],
)
@pytest.mark.parametrize("service_call", [SERVICE_TURN_ON, SERVICE_TURN_OFF])
async def test_turn_off_on(
    hass: HomeAssistant,
    mock_portainer_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    entity_id: str,
    turn_on_method: str,
    turn_off_method: str,
    expected_args: tuple,
    service_call: str,
) -> None:
    """Test the switches. Have you tried to turn it off and on again?"""
    await setup_integration(hass, mock_config_entry)

    client_method = (
        turn_on_method if service_call == SERVICE_TURN_ON else turn_off_method
    )
    method_mock = getattr(mock_portainer_client, client_method)

    await hass.services.async_call(
        SWITCH_DOMAIN,
        service_call,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    method_mock.assert_called_once_with(*expected_args)


@pytest.mark.parametrize(
    ("entity_id", "turn_on_method", "turn_off_method"),
    [
        (
            "switch.practical_morse_container",
            "start_container",
            "stop_container",
        ),
        (
            "switch.webstack_stack",
            "start_stack",
            "stop_stack",
        ),
    ],
)
@pytest.mark.parametrize("service_call", [SERVICE_TURN_ON, SERVICE_TURN_OFF])
@pytest.mark.parametrize(
    ("raise_exception", "expected_exception"),
    [
        (PortainerAuthenticationError, HomeAssistantError),
        (PortainerConnectionError, HomeAssistantError),
        (PortainerTimeoutError, HomeAssistantError),
    ],
)
async def test_turn_off_on_exceptions(
    hass: HomeAssistant,
    mock_portainer_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    entity_id: str,
    turn_on_method: str,
    turn_off_method: str,
    service_call: str,
    raise_exception: Exception,
    expected_exception: Exception,
) -> None:
    """Test switch actions raise on client errors."""
    await setup_integration(hass, mock_config_entry)

    client_method = (
        turn_on_method if service_call == SERVICE_TURN_ON else turn_off_method
    )
    method_mock = getattr(mock_portainer_client, client_method)

    method_mock.side_effect = raise_exception
    with pytest.raises(expected_exception):
        await hass.services.async_call(
            SWITCH_DOMAIN,
            service_call,
            {ATTR_ENTITY_ID: entity_id},
            blocking=True,
        )
