"""Tests for the aws component config and setup."""
import json
from unittest.mock import AsyncMock, MagicMock, call, patch as async_patch

from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component


class MockAioSession:
    """Mock AioSession."""

    def __init__(self, *args, **kwargs):
        """Init a mock session."""
        self.get_user = AsyncMock()
        self.invoke = AsyncMock()
        self.publish = AsyncMock()
        self.send_message = AsyncMock()
        self.put_events = AsyncMock()

    def create_client(self, *args, **kwargs):
        """Create a mocked client."""
        return MagicMock(
            __aenter__=AsyncMock(
                return_value=AsyncMock(
                    get_user=self.get_user,  # iam
                    invoke=self.invoke,  # lambda
                    publish=self.publish,  # sns
                    send_message=self.send_message,  # sqs
                    put_events=self.put_events,  # events
                )
            ),
            __aexit__=AsyncMock(),
        )

    async def get_available_regions(self, *args, **kwargs):
        """Return list of available regions."""
        return ["us-east-1", "us-east-2", "us-west-1", "us-west-2"]


async def test_empty_config(hass: HomeAssistant) -> None:
    """Test a default config will be create for empty config."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(hass, "aws", {"aws": {}})
        await hass.async_block_till_done()

    # we don't validate auto-created default profile
    mock_session.get_user.assert_not_awaited()


async def test_empty_credential(hass: HomeAssistant) -> None:
    """Test a default config will be create for empty credential section."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "notify": [
                        {
                            "service": "lambda",
                            "name": "New Lambda Test",
                            "region_name": "us-east-1",
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "new_lambda_test") is True
    await hass.services.async_call(
        "notify", "new_lambda_test", {"message": "test", "target": "ARN"}, blocking=True
    )
    mock_session.invoke.assert_awaited_once()


async def test_profile_credential(hass: HomeAssistant) -> None:
    """Test credentials with profile name."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "credentials": {"name": "test", "profile_name": "test-profile"},
                    "notify": [
                        {
                            "service": "sns",
                            "credential_name": "test",
                            "name": "SNS Test",
                            "region_name": "us-east-1",
                        }
                    ],
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "sns_test") is True
    await hass.services.async_call(
        "notify",
        "sns_test",
        {"title": "test", "message": "test", "target": "ARN"},
        blocking=True,
    )
    mock_session.publish.assert_awaited_once()


async def test_access_key_credential(hass: HomeAssistant) -> None:
    """Test credentials with access key."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "credentials": [
                        {"name": "test", "profile_name": "test-profile"},
                        {
                            "name": "key",
                            "aws_access_key_id": "test-key",
                            "aws_secret_access_key": "test-secret",
                        },
                    ],
                    "notify": [
                        {
                            "service": "sns",
                            "credential_name": "key",
                            "name": "SNS Test",
                            "region_name": "us-east-1",
                        }
                    ],
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "sns_test") is True
    await hass.services.async_call(
        "notify",
        "sns_test",
        {"title": "test", "message": "test", "target": "ARN"},
        blocking=True,
    )
    mock_session.publish.assert_awaited_once()


async def test_notify_credential(hass: HomeAssistant) -> None:
    """Test notify service can use access key directly."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ), async_patch(
        "homeassistant.components.aws.notify.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "notify": [
                        {
                            "service": "sqs",
                            "credential_name": "test",
                            "name": "SQS Test",
                            "region_name": "us-east-1",
                            "aws_access_key_id": "some-key",
                            "aws_secret_access_key": "some-secret",
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "sqs_test") is True
    await hass.services.async_call(
        "notify", "sqs_test", {"message": "test", "target": "ARN"}, blocking=True
    )


async def test_notify_credential_profile(hass: HomeAssistant) -> None:
    """Test notify service can use profile directly."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ), async_patch(
        "homeassistant.components.aws.notify.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "notify": [
                        {
                            "service": "sqs",
                            "name": "SQS Test",
                            "region_name": "us-east-1",
                            "profile_name": "test",
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "sqs_test") is True
    await hass.services.async_call(
        "notify", "sqs_test", {"message": "test", "target": "ARN"}, blocking=True
    )


async def test_credential_skip_validate(hass: HomeAssistant) -> None:
    """Test credential can skip validate."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "credentials": [
                        {
                            "name": "key",
                            "aws_access_key_id": "not-valid",
                            "aws_secret_access_key": "dont-care",
                            "validate": False,
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    mock_session.get_user.assert_not_awaited()


async def test_service_call_extra_data(hass: HomeAssistant) -> None:
    """Test service call extra data are parsed properly."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "notify": [
                        {
                            "service": "sns",
                            "name": "SNS Test",
                            "region_name": "us-east-1",
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "sns_test") is True
    await hass.services.async_call(
        "notify",
        "sns_test",
        {
            "message": "test",
            "target": "ARN",
            "data": {"AWS.SNS.SMS.SenderID": "HA-notify"},
        },
        blocking=True,
    )
    mock_session.publish.assert_called_once_with(
        TargetArn="ARN",
        Message="test",
        Subject="Home Assistant",
        MessageAttributes={
            "AWS.SNS.SMS.SenderID": {"StringValue": "HA-notify", "DataType": "String"}
        },
    )


async def test_events_service_call(hass: HomeAssistant) -> None:
    """Test events service (EventBridge) call works as expected."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "notify": [
                        {
                            "service": "events",
                            "name": "Events Test",
                            "region_name": "us-east-1",
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "events_test") is True

    mock_session.put_events.return_value = {
        "Entries": [{"EventId": "", "ErrorCode": 0, "ErrorMessage": "test-error"}]
    }

    await hass.services.async_call(
        "notify",
        "events_test",
        {
            "message": "test",
            "target": "ARN",
            "data": {},
        },
        blocking=True,
    )

    mock_session.put_events.assert_called_once_with(
        Entries=[
            {
                "EventBusName": "ARN",
                "Detail": json.dumps({"message": "test"}),
                "DetailType": "",
                "Source": "homeassistant",
                "Resources": [],
            }
        ]
    )


async def test_events_service_call_10_targets(hass: HomeAssistant) -> None:
    """Test events service (EventBridge) call works with more than 10 targets."""
    mock_session = MockAioSession()
    with async_patch(
        "homeassistant.components.aws.AioSession", return_value=mock_session
    ):
        await async_setup_component(
            hass,
            "aws",
            {
                "aws": {
                    "notify": [
                        {
                            "service": "events",
                            "name": "Events Test",
                            "region_name": "us-east-1",
                        }
                    ]
                }
            },
        )
        await hass.async_block_till_done()

    assert hass.services.has_service("notify", "events_test") is True
    await hass.services.async_call(
        "notify",
        "events_test",
        {
            "message": "",
            "target": [f"eventbus{i}" for i in range(11)],
            "data": {
                "detail_type": "test_event",
                "detail": {"eventkey": "eventvalue"},
                "source": "HomeAssistant-test",
                "resources": ["resource1", "resource2"],
            },
        },
        blocking=True,
    )

    entry = {
        "Detail": json.dumps({"eventkey": "eventvalue"}),
        "DetailType": "test_event",
        "Source": "HomeAssistant-test",
        "Resources": ["resource1", "resource2"],
    }

    mock_session.put_events.assert_has_calls(
        [
            call(Entries=[entry | {"EventBusName": f"eventbus{i}"} for i in range(10)]),
            call(Entries=[entry | {"EventBusName": "eventbus10"}]),
        ]
    )