from __future__ import annotations

from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field
from datetime import datetime
from typing import Protocol, runtime_checkable


CAPABILITY_CALENDAR_SCHEDULING = "calendar.scheduling"
CAPABILITY_CALENDAR_OUTBOX = "calendar.outbox"
CALENDAR_AVAILABILITY_READ_SCOPE = "calendar:availability:read"
CALENDAR_EVENT_WRITE_SCOPE = "calendar:event:write"


class CalendarCapabilityError(ValueError):
    """Stable error raised by Calendar capability implementations."""


@dataclass(frozen=True, slots=True)
class CalendarEventRequest:
    summary: str
    start_at: datetime
    calendar_id: str | None = None
    description: str | None = None
    location: str | None = None
    status: str = "CONFIRMED"
    transparency: str = "OPAQUE"
    classification: str = "PUBLIC"
    end_at: datetime | None = None
    timezone: str | None = None
    attendees: tuple[Mapping[str, object], ...] = ()
    categories: tuple[str, ...] = ()
    related_to: tuple[Mapping[str, object], ...] = ()
    metadata: Mapping[str, object] = field(default_factory=dict)


@dataclass(frozen=True, slots=True)
class CalendarEventRef:
    id: str
    calendar_id: str
    uid: str
    external_state: str = "local"
    outbox_operation_id: str | None = None


@runtime_checkable
class CalendarSchedulingProvider(Protocol):
    def list_freebusy(
        self,
        session: object,
        *,
        tenant_id: str,
        start_at: datetime,
        end_at: datetime,
        calendar_ids: Sequence[str] | None = None,
    ) -> Sequence[Mapping[str, object]]:
        ...

    def create_event(
        self,
        session: object,
        *,
        tenant_id: str,
        user_id: str | None,
        request: CalendarEventRequest,
    ) -> CalendarEventRef:
        ...


@runtime_checkable
class CalendarOutboxProvider(Protocol):
    """Stable worker boundary for Calendar-owned external side effects."""

    def dispatch_due(
        self,
        session: object,
        *,
        tenant_id: str | None = None,
        limit: int = 50,
    ) -> Mapping[str, object]:
        ...

def calendar_scheduling_provider(registry: object | None) -> CalendarSchedulingProvider | None:
    if registry is None or not hasattr(registry, "has_capability"):
        return None
    if not registry.has_capability(CAPABILITY_CALENDAR_SCHEDULING):
        return None
    capability = registry.capability(CAPABILITY_CALENDAR_SCHEDULING)
    return capability if isinstance(capability, CalendarSchedulingProvider) else None


def calendar_outbox_provider(registry: object | None) -> CalendarOutboxProvider | None:
    if registry is None or not hasattr(registry, "has_capability"):
        return None
    if not registry.has_capability(CAPABILITY_CALENDAR_OUTBOX):
        return None
    capability = registry.capability(CAPABILITY_CALENDAR_OUTBOX)
    return capability if isinstance(capability, CalendarOutboxProvider) else None
