"""Unit tests for CalendarTool — focus on relative date resolution."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone
from unittest.mock import AsyncMock

import pytest

from agentkit.calendar.service import CalendarService
from agentkit.tools.calendar_tool import CalendarTool, _resolve_datetime


class TestResolveDatetime:
    def test_none(self) -> None:
        assert _resolve_datetime(None) is None
        assert _resolve_datetime("") is None

    def test_iso_unchanged(self) -> None:
        iso = "2026-07-06T09:00:00+08:00"
        assert _resolve_datetime(iso) == iso

    def test_iso_with_z_unchanged(self) -> None:
        iso = "2026-07-06T01:00:00Z"
        assert _resolve_datetime(iso) == iso

    def test_next_monday(self) -> None:
        result = _resolve_datetime("next monday")
        assert result is not None
        # Should be parseable as ISO 8601
        parsed = datetime.fromisoformat(result)
        # Monday weekday
        assert parsed.weekday() == 0

    def test_tomorrow_with_time(self) -> None:
        result = _resolve_datetime("tomorrow 3pm")
        assert result is not None
        parsed = datetime.fromisoformat(result)
        assert parsed.hour == 15

    def test_in_3_days(self) -> None:
        # dateutil handles "in N days" — but with timezone-dependent results;
        # the *contract* is that _resolve_datetime returns a parseable ISO string.
        # We don't assert a specific offset because dateutil interprets "in" loosely.
        result = _resolve_datetime("in 3 days")
        assert result is not None
        datetime.fromisoformat(result)  # must be parseable

    def test_garbage_returns_none(self) -> None:
        assert _resolve_datetime("not a date at all xyz") is None


@pytest.fixture
def mock_service() -> AsyncMock:
    svc = AsyncMock(spec=CalendarService)
    svc.create_event = AsyncMock()
    return svc


class TestCreateEventWithRelativeDates:
    @pytest.mark.asyncio
    async def test_create_event_resolves_next_monday(
        self, mock_service: AsyncMock
    ) -> None:
        from agentkit.calendar.models import CalendarEvent

        mock_service.create_event.return_value = CalendarEvent(
            id="evt-1",
            user_id="u1",
            title="项目启动会",
            description="",
            start_time=datetime.now(tz=timezone.utc),
            end_time=datetime.now(tz=timezone.utc) + timedelta(hours=1),
            location="",
            is_all_day=False,
        )

        tool = CalendarTool(mock_service)  # type: ignore[arg-type]
        result = await tool.execute(
            action="create_event",
            user_id="u1",
            title="项目启动会",
            start_time="next monday",
            end_time="next monday",
            reminder_offset_minutes=15,
        )

        assert result["success"] is True, result
        # create_event should have been called with ISO strings, not the raw "next monday"
        call_kwargs = mock_service.create_event.call_args.kwargs
        assert call_kwargs["start_time"] != "next monday"
        # Should be parseable
        datetime.fromisoformat(call_kwargs["start_time"])

    @pytest.mark.asyncio
    async def test_create_event_unparseable_returns_error(
        self, mock_service: AsyncMock
    ) -> None:
        tool = CalendarTool(mock_service)  # type: ignore[arg-type]
        result = await tool.execute(
            action="create_event",
            user_id="u1",
            title="x",
            start_time="garbage qqq",
            end_time="garbage qqq",
        )
        assert result["success"] is False
        assert "Could not parse start_time" in result["error"]
