from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from arcade.sdk import ToolContext

from arcade_google.tools import create_contact


@pytest.fixture
def mock_context():
    context = AsyncMock(spec=ToolContext)
    context.authorization = MagicMock()
    context.authorization.token = "mock_token"  # noqa: S105
    return context


@pytest.mark.asyncio
async def test_create_contact_success(mock_context):
    # Test create_contact with all parameters (given, family names and email)
    created_contact_data = {"resourceName": "people/123", "etag": "abc"}

    create_contact_call = MagicMock()
    create_contact_call.execute.return_value = created_contact_data

    people_mock = MagicMock()
    people_mock.createContact.return_value = create_contact_call

    service_mock = MagicMock()
    service_mock.people.return_value = people_mock

    with patch(
        "arcade_google.tools.contacts.build_people_service", return_value=service_mock
    ) as mock_build:
        result = await create_contact(
            mock_context,
            given_name="Alice",
            family_name="Smith",
            email="alice@example.com",
        )
        assert "contact" in result
        assert result["contact"] == created_contact_data

        # Verify that the createContact API was called with the correct body contents.
        expected_body = {
            "names": [{"givenName": "Alice", "familyName": "Smith"}],
            "emailAddresses": [{"value": "alice@example.com", "type": "work"}],
        }
        people_mock.createContact.assert_called_once_with(
            body=expected_body, personFields="names,emailAddresses"
        )
        mock_build.assert_called_once()


@pytest.mark.asyncio
async def test_create_contact_success_without_optional(mock_context):
    # Test create_contact without optional parameters family_name and email.
    created_contact_data = {"resourceName": "people/456", "etag": "def"}

    create_contact_call = MagicMock()
    create_contact_call.execute.return_value = created_contact_data

    people_mock = MagicMock()
    people_mock.createContact.return_value = create_contact_call

    service_mock = MagicMock()
    service_mock.people.return_value = people_mock

    with patch("arcade_google.tools.contacts.build_people_service", return_value=service_mock):
        result = await create_contact(mock_context, given_name="Bob", family_name=None, email=None)
        assert "contact" in result
        assert result["contact"] == created_contact_data

        # Expected body should only include the givenName when family_name and email are omitted.
        expected_body = {"names": [{"givenName": "Bob"}]}
        people_mock.createContact.assert_called_once_with(
            body=expected_body, personFields="names,emailAddresses"
        )


@pytest.mark.asyncio
async def test_create_contact_error(mock_context):
    # Simulate an error thrown by createContact
    error_call = MagicMock()
    error_call.execute.side_effect = Exception("Create error")

    people_mock = MagicMock()
    people_mock.createContact.return_value = error_call

    service_mock = MagicMock()
    service_mock.people.return_value = people_mock

    with (
        patch("arcade_google.tools.contacts.build_people_service", return_value=service_mock),
        pytest.raises(Exception, match="Error in execution of CreateContact"),
    ):
        await create_contact(mock_context, given_name="Alice", family_name="Doe", email=None)
