"""Calendar service — unified interface for Google, Yandex, Apple CalDAV. Manages calendar provider connections and event creation. """ import json import logging from datetime import datetime, timedelta, timezone from typing import Any, Optional from uuid import UUID from sqlalchemy.ext.asyncio import AsyncSession from app.models.user import User logger = logging.getLogger(__name__) CALENDAR_PROVIDERS = ("google", "yandex", "apple") class CalendarService: def __init__(self, db: AsyncSession): self.db = db async def get_calendar_credentials(self, user: User) -> Optional[dict[str, Any]]: """Get stored calendar credentials for user.""" if not hasattr(user, "calendar_provider") or not user.calendar_provider: return None if not hasattr(user, "calendar_credentials") or not user.calendar_credentials: return None try: return json.loads(user.calendar_credentials) except (json.JSONDecodeError, TypeError): return None async def set_calendar_credentials( self, user: User, provider: str, credentials: dict[str, Any] ) -> None: user.calendar_provider = provider user.calendar_credentials = json.dumps(credentials) await self.db.commit() async def disconnect_calendar(self, user: User) -> None: user.calendar_provider = None user.calendar_credentials = None await self.db.commit() def _get_client(self, provider: str, credentials: dict[str, Any]): """Get appropriate calendar client for provider.""" if provider == "google": from app.integrations.calendar.google_calendar import GoogleCalendarClient return GoogleCalendarClient(access_token=credentials.get("access_token", "")) elif provider == "yandex": from app.integrations.calendar.yandex_calendar import YandexCalendarClient return YandexCalendarClient(access_token=credentials.get("access_token", "")) elif provider == "apple": from app.integrations.calendar.apple_caldav import AppleCalDavStub return AppleCalDavStub() raise ValueError(f"Unknown calendar provider: {provider}") async def create_idea_event(self, user: User, idea_title: str, idea_content: str) -> bool: """Create a calendar event from an idea.""" creds = await self.get_calendar_credentials(user) if not creds: logger.warning("No calendar credentials for user %s", user.id) return False provider = user.calendar_provider if not provider: return False client = self._get_client(provider, creds) now = datetime.now(timezone.utc) start_iso = now.isoformat() end_iso = (now + timedelta(hours=1)).isoformat() return await client.create_event( summary=f"Idea: {idea_title[:100]}", description=idea_content[:1000], start_iso=start_iso, end_iso=end_iso, ) async def list_events(self, user: User, max_results: int = 10) -> list[dict]: creds = await self.get_calendar_credentials(user) if not creds or not user.calendar_provider: return [] client = self._get_client(user.calendar_provider, creds) return await client.list_events(max_results=max_results) async def health_check(self, user: User) -> bool: creds = await self.get_calendar_credentials(user) if not creds or not user.calendar_provider: return False try: client = self._get_client(user.calendar_provider, creds) return await client.health_check() except Exception: return False