"""Tests for Google Calendar adapter."""

from __future__ import annotations

from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock, MagicMock, patch

import pytest


class TestGoogleCalendarAdapterListEvents:
    """Tests for GoogleCalendarAdapter.list_events."""

    @pytest.mark.asyncio
    async def test_list_events_returns_calendar_events(self) -> None:
        """list_events should return parsed calendar events."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        now = datetime.now(UTC)
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "items": [
                {
                    "id": "event-1",
                    "summary": "Team Standup",
                    "start": {"dateTime": (now + timedelta(hours=1)).isoformat()},
                    "end": {"dateTime": (now + timedelta(hours=2)).isoformat()},
                    "attendees": [
                        {"email": "alice@example.com"},
                        {"email": "bob@example.com"},
                    ],
                    "hangoutLink": "https://meet.google.com/abc-defg-hij",
                },
                {
                    "id": "event-2",
                    "summary": "All-Day Planning",
                    "start": {"date": now.strftime("%Y-%m-%d")},
                    "end": {"date": (now + timedelta(days=1)).strftime("%Y-%m-%d")},
                },
            ]
        }

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            events = await adapter.list_events("access-token", hours_ahead=24, limit=10)

        assert len(events) == 2, "should return 2 events"
        assert events[0].title == "Team Standup", "first event title should match"
        assert events[0].attendees == ("alice@example.com", "bob@example.com"), "attendees should match"
        assert events[0].meeting_url == "https://meet.google.com/abc-defg-hij", "meeting_url should match"
        assert events[0].provider == "google", "provider should be 'google'"
        assert events[1].title == "All-Day Planning", "second event title should match"
        assert events[1].is_all_day is True, "all-day event should be marked"

    @pytest.mark.asyncio
    async def test_list_events_handles_empty_response(self) -> None:
        """list_events should return empty list when no events."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"items": []}

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            events = await adapter.list_events("access-token")

        assert events == [], "empty response should yield empty list"

    @pytest.mark.asyncio
    async def test_list_events_raises_on_expired_token(self) -> None:
        """list_events should raise error on 401 response."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter
        from noteflow.infrastructure.calendar.google_adapter import GoogleCalendarError

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 401
        mock_response.text = "Token expired"

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response

            with pytest.raises(GoogleCalendarError, match="expired or invalid"):
                await adapter.list_events("expired-token")

    @pytest.mark.asyncio
    async def test_list_events_raises_on_api_error(self) -> None:
        """list_events should raise error on non-200 response."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter
        from noteflow.infrastructure.calendar.google_adapter import GoogleCalendarError

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 500
        mock_response.text = "Internal server error"

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response

            with pytest.raises(GoogleCalendarError, match="API error"):
                await adapter.list_events("access-token")

    @pytest.mark.asyncio
    async def test_list_events_parses_conference_data(self) -> None:
        """list_events should extract meeting URL from conferenceData."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        now = datetime.now(UTC)
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "items": [
                {
                    "id": "event-zoom",
                    "summary": "Zoom Meeting",
                    "start": {"dateTime": now.isoformat()},
                    "end": {"dateTime": (now + timedelta(hours=1)).isoformat()},
                    "conferenceData": {
                        "entryPoints": [
                            {"entryPointType": "video", "uri": "https://zoom.us/j/123456"},
                            {"entryPointType": "phone", "uri": "tel:+1234567890"},
                        ]
                    },
                }
            ]
        }

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            events = await adapter.list_events("access-token")

        assert events[0].meeting_url == "https://zoom.us/j/123456", "should extract video URL"


class TestGoogleCalendarAdapterGetUserEmail:
    """Tests for GoogleCalendarAdapter.get_user_email."""

    @pytest.mark.asyncio
    async def test_get_user_email_returns_email(self) -> None:
        """get_user_email should return user's email address."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "email": "user@example.com",
            "name": "Test User",
        }

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            email = await adapter.get_user_email("access-token")

        assert email == "user@example.com", "should return user email"

    @pytest.mark.asyncio
    async def test_get_user_email_raises_on_missing_email(self) -> None:
        """get_user_email should raise when email not in response."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter
        from noteflow.infrastructure.calendar.google_adapter import GoogleCalendarError

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"name": "No Email User"}

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response

            with pytest.raises(GoogleCalendarError, match="No email"):
                await adapter.get_user_email("access-token")


class TestGoogleCalendarAdapterDateParsing:
    """Tests for date/time parsing in GoogleCalendarAdapter."""

    @pytest.mark.asyncio
    async def test_parses_utc_datetime_with_z_suffix(self) -> None:
        """Should parse datetime with Z suffix correctly."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "items": [
                {
                    "id": "event-utc",
                    "summary": "UTC Event",
                    "start": {"dateTime": "2024-03-15T10:00:00Z"},
                    "end": {"dateTime": "2024-03-15T11:00:00Z"},
                }
            ]
        }

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            events = await adapter.list_events("access-token")

        assert events[0].start_time.tzinfo is not None, "should have timezone info"
        assert events[0].start_time.hour == 10, "hour should be parsed correctly"

    @pytest.mark.asyncio
    async def test_parses_datetime_with_offset(self) -> None:
        """Should parse datetime with timezone offset correctly."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "items": [
                {
                    "id": "event-offset",
                    "summary": "Offset Event",
                    "start": {"dateTime": "2024-03-15T10:00:00-08:00"},
                    "end": {"dateTime": "2024-03-15T11:00:00-08:00"},
                }
            ]
        }

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            events = await adapter.list_events("access-token")

        assert events[0].start_time.tzinfo is not None, "offset datetime should have tzinfo"

    @pytest.mark.asyncio
    async def test_identifies_recurring_events(self) -> None:
        """Should identify recurring events via recurringEventId."""
        from noteflow.infrastructure.calendar import GoogleCalendarAdapter

        adapter = GoogleCalendarAdapter()

        now = datetime.now(UTC)
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "items": [
                {
                    "id": "event-instance",
                    "summary": "Weekly Meeting",
                    "start": {"dateTime": now.isoformat()},
                    "end": {"dateTime": (now + timedelta(hours=1)).isoformat()},
                    "recurringEventId": "event-master",
                }
            ]
        }

        with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
            mock_get.return_value = mock_response
            events = await adapter.list_events("access-token")

        assert events[0].is_recurring is True, "event with recurringEventId should be recurring"
