"""
Calendar CalDAV Provider Tests
==============================
Tests the multi-provider calendar dispatch logic:
  - _calendar_provider() with various env combos (explicit, auto-detect, none)
  - Dispatch: get_events() routes to CalDAV vs Google based on provider
  - "none" case returns helpful configuration message
  - CalDAV functions use caldav.DAVClient (mocked — no real server needed)

Run with:
    pytest familiar/tests/test_calendar_caldav.py -v
"""

import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

sys.path.insert(0, str(Path(__file__).parent.parent))


# ---------------------------------------------------------------------------
# _calendar_provider() — provider detection
# ---------------------------------------------------------------------------


class TestCalendarProvider:
    """Test _calendar_provider() returns correct backend string."""

    def test_explicit_caldav_with_env(self, monkeypatch):
        """CALENDAR_PROVIDER=caldav with valid env → 'caldav'."""
        monkeypatch.setenv("CALENDAR_PROVIDER", "caldav")
        monkeypatch.setenv("CALDAV_URL", "https://cloud.example.org/dav/")
        monkeypatch.setenv("CALDAV_USER", "alice")
        monkeypatch.setenv("CALDAV_PASSWORD", "secret")

        with patch(
            "familiar.skills.calendar.skill.importlib.util.find_spec",
            return_value=MagicMock(),
        ):
            from familiar.skills.calendar.skill import _calendar_provider

            assert _calendar_provider() == "caldav"

    def test_explicit_google_with_token(self, monkeypatch, tmp_path):
        """CALENDAR_PROVIDER=google with token file → 'google'."""
        monkeypatch.setenv("CALENDAR_PROVIDER", "google")

        # Create fake token file
        token = tmp_path / "google_token.json"
        token.write_text("{}")

        with (
            patch("familiar.skills.calendar.skill.TOKEN_FILE", token),
            patch(
                "familiar.skills.calendar.skill.importlib.util.find_spec",
                return_value=MagicMock(),
            ),
        ):
            from familiar.skills.calendar.skill import _calendar_provider

            assert _calendar_provider() == "google"

    def test_auto_detect_caldav_when_no_google(self, monkeypatch):
        """No Google token, CalDAV env set → auto-detects 'caldav'."""
        monkeypatch.delenv("CALENDAR_PROVIDER", raising=False)
        monkeypatch.setenv("CALDAV_URL", "https://cloud.example.org/dav/")
        monkeypatch.setenv("CALDAV_USER", "alice")
        monkeypatch.setenv("CALDAV_PASSWORD", "secret")

        def fake_find_spec(mod):
            # caldav and vobject are available, Google libs are not
            if mod in ("caldav", "vobject"):
                return MagicMock()
            return None

        with (
            patch("familiar.skills.calendar.skill.TOKEN_FILE", Path("/nonexistent/token.json")),
            patch(
                "familiar.skills.calendar.skill.importlib.util.find_spec",
                side_effect=fake_find_spec,
            ),
        ):
            from familiar.skills.calendar.skill import _calendar_provider

            assert _calendar_provider() == "caldav"

    def test_none_when_nothing_configured(self, monkeypatch):
        """No Google token, no CalDAV env → 'none'."""
        monkeypatch.delenv("CALENDAR_PROVIDER", raising=False)
        monkeypatch.delenv("CALDAV_URL", raising=False)
        monkeypatch.delenv("CALDAV_USER", raising=False)
        monkeypatch.delenv("CALDAV_PASSWORD", raising=False)

        with (
            patch("familiar.skills.calendar.skill.TOKEN_FILE", Path("/nonexistent/token.json")),
            patch(
                "familiar.skills.calendar.skill.importlib.util.find_spec",
                return_value=None,
            ),
        ):
            from familiar.skills.calendar.skill import _calendar_provider

            assert _calendar_provider() == "none"

    def test_auto_detect_prefers_google(self, monkeypatch, tmp_path):
        """Both available → auto-detects 'google' (existing default preferred)."""
        monkeypatch.delenv("CALENDAR_PROVIDER", raising=False)
        monkeypatch.setenv("CALDAV_URL", "https://cloud.example.org/dav/")
        monkeypatch.setenv("CALDAV_USER", "alice")
        monkeypatch.setenv("CALDAV_PASSWORD", "secret")

        token = tmp_path / "google_token.json"
        token.write_text("{}")

        with (
            patch("familiar.skills.calendar.skill.TOKEN_FILE", token),
            patch(
                "familiar.skills.calendar.skill.importlib.util.find_spec",
                return_value=MagicMock(),
            ),
        ):
            from familiar.skills.calendar.skill import _calendar_provider

            assert _calendar_provider() == "google"


# ---------------------------------------------------------------------------
# Dispatch — get_events routes correctly
# ---------------------------------------------------------------------------


class TestCalendarDispatch:
    """Test that public handlers dispatch to the right backend (legacy path)."""

    # Patch get_all_accounts to return [] so we hit the legacy fallback
    _NO_ACCOUNTS = patch("familiar.skills.calendar.accounts.get_all_accounts", return_value=[])

    def test_get_events_routes_to_caldav(self, monkeypatch):
        """When provider is 'caldav', get_events calls CalDAV path."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="caldav",
            ),
            patch(
                "familiar.skills.calendar.skill._caldav_get_events",
                return_value="CalDAV events here",
            ) as mock_caldav,
        ):
            from familiar.skills.calendar.skill import get_events

            result = get_events({"date": "today"})
            mock_caldav.assert_called_once_with({"date": "today"})
            assert result == "CalDAV events here"

    def test_get_events_none_returns_message(self, monkeypatch):
        """When provider is 'none', get_events returns config message."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="none",
            ),
        ):
            from familiar.skills.calendar.skill import get_events

            result = get_events({"date": "today"})
            assert "/connect calendar" in result

    def test_create_event_routes_to_caldav(self):
        """create_event dispatches to CalDAV backend."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="caldav",
            ),
            patch(
                "familiar.skills.calendar.skill._caldav_create_event",
                return_value="Created via CalDAV",
            ) as mock_caldav,
        ):
            from familiar.skills.calendar.skill import create_event

            data = {"title": "Test", "start": "2026-03-01 10:00", "_confirmed": True}
            result = create_event(data)
            mock_caldav.assert_called_once_with(data)
            assert result == "Created via CalDAV"

    def test_check_availability_routes_to_caldav(self):
        """check_availability dispatches to CalDAV backend."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="caldav",
            ),
            patch(
                "familiar.skills.calendar.skill._caldav_check_availability",
                return_value="Available",
            ) as mock_caldav,
        ):
            from familiar.skills.calendar.skill import check_availability

            data = {"date": "today", "time": "10:00"}
            check_availability(data)
            mock_caldav.assert_called_once_with(data)

    def test_find_free_time_routes_to_caldav(self):
        """find_free_time dispatches to CalDAV backend."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="caldav",
            ),
            patch(
                "familiar.skills.calendar.skill._caldav_find_free_time",
                return_value="Free slots",
            ) as mock_caldav,
        ):
            from familiar.skills.calendar.skill import find_free_time

            data = {"date": "today"}
            find_free_time(data)
            mock_caldav.assert_called_once_with(data)

    def test_delete_event_routes_to_caldav(self):
        """delete_event dispatches to CalDAV backend."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="caldav",
            ),
            patch(
                "familiar.skills.calendar.skill._caldav_delete_event",
                return_value="Deleted",
            ) as mock_caldav,
        ):
            from familiar.skills.calendar.skill import delete_event

            data = {"search": "Test", "date": "today", "_confirmed": True}
            delete_event(data)
            mock_caldav.assert_called_once_with(data)

    def test_delete_event_none_returns_message(self):
        """delete_event returns config message when no provider."""
        with (
            self._NO_ACCOUNTS,
            patch(
                "familiar.skills.calendar.skill._calendar_provider",
                return_value="none",
            ),
        ):
            from familiar.skills.calendar.skill import delete_event

            result = delete_event({"search": "Test"})
            assert "/connect calendar" in result


# ---------------------------------------------------------------------------
# CalDAV get_events — end-to-end with mock client
# ---------------------------------------------------------------------------


class TestCalDAVGetEvents:
    """Test _caldav_get_events with a mocked DAVClient."""

    def test_returns_events_from_caldav(self, monkeypatch):
        """CalDAV get_events parses events from CalDAV server correctly."""
        from datetime import datetime

        parsed_events = [
            {
                "title": "Team Standup",
                "start": datetime(2026, 3, 1, 10, 0),
                "end": datetime(2026, 3, 1, 10, 30),
            }
        ]

        mock_event = MagicMock()
        mock_event.data = "VCALENDAR_DATA"

        mock_calendar = MagicMock()
        mock_calendar.date_search.return_value = [mock_event]

        mock_principal = MagicMock()
        mock_principal.calendars.return_value = [mock_calendar]

        mock_client = MagicMock()
        mock_client.principal.return_value = mock_principal

        with (
            patch(
                "familiar.skills.calendar.skill._get_caldav_client",
                return_value=mock_client,
            ),
            patch(
                "familiar.skills.calendar.skill._parse_vevent",
                return_value=parsed_events,
            ),
        ):
            from familiar.skills.calendar.skill import _caldav_get_events

            result = _caldav_get_events({"date": "today"})
            assert "Team Standup" in result

    def test_no_events_message(self, monkeypatch):
        """Returns 'no events' when CalDAV has none."""
        mock_calendar = MagicMock()
        mock_calendar.date_search.return_value = []

        mock_principal = MagicMock()
        mock_principal.calendars.return_value = [mock_calendar]

        mock_client = MagicMock()
        mock_client.principal.return_value = mock_principal

        with patch(
            "familiar.skills.calendar.skill._get_caldav_client",
            return_value=mock_client,
        ):
            from familiar.skills.calendar.skill import _caldav_get_events

            result = _caldav_get_events({"date": "today"})
            assert "No events" in result
