# Calendar Sync Feature Integration Guide

This document explains how the calendar sync feature permission system works and provides examples for implementation.

## Overview

The calendar sync feature allows tenants to:
1. Connect to Google Calendar or Outlook Calendar via OAuth
2. Sync calendar events into the scheduling system
3. Manage multiple calendar integrations

The feature is gated by the `can_use_calendar_sync` permission that must be enabled at the tenant level.

## Architecture

### 1. Database Model (Tenant)

Added to `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/models.py`:

```python
can_use_calendar_sync = models.BooleanField(
    default=False,
    help_text="Whether this business can sync Google Calendar and other calendar providers"
)
```

### 2. Permission Check (HasFeaturePermission)

Updated in `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/permissions.py`:

```python
FEATURE_NAMES = {
    # ... other features ...
    'can_use_calendar_sync': 'Calendar Sync',
}
```

The `HasFeaturePermission` factory function creates a DRF permission class:

```python
from core.permissions import HasFeaturePermission

permission_classes = [IsAuthenticated, HasFeaturePermission('can_use_calendar_sync')]
```

### 3. OAuth Integration

Modified in `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/oauth_views.py`:

- `GoogleOAuthInitiateView`: Checks permission when purpose is "calendar"
- `MicrosoftOAuthInitiateView`: Checks permission when purpose is "calendar"

```python
def post(self, request):
    purpose = request.data.get('purpose', 'email')

    # Check calendar sync permission if purpose is calendar
    if purpose == 'calendar':
        calendar_permission = HasFeaturePermission('can_use_calendar_sync')
        if not calendar_permission().has_permission(request, self):
            return Response({
                'success': False,
                'error': 'Your current plan does not include Calendar Sync.',
            }, status=status.HTTP_403_FORBIDDEN)

    # Continue with OAuth flow...
```

### 4. Calendar Sync Views

Created in `/home/poduck/Desktop/smoothschedule2/smoothschedule/schedule/calendar_sync_views.py`:

- `CalendarListView`: Lists connected calendars
- `CalendarSyncView`: Syncs calendar events
- `CalendarDeleteView`: Disconnects calendar integration
- `CalendarStatusView`: Gets calendar sync status

#### Permission Pattern in Views

```python
from core.permissions import HasFeaturePermission
from rest_framework.permissions import IsAuthenticated

class CalendarSyncPermission(IsAuthenticated):
    """Custom permission combining auth + feature check"""
    def has_permission(self, request, view):
        if not super().has_permission(request, view):
            return False

        tenant = getattr(request, 'tenant', None)
        if not tenant:
            return False

        return tenant.has_feature('can_use_calendar_sync')


class CalendarListView(APIView):
    permission_classes = [CalendarSyncPermission]

    def get(self, request):
        # This endpoint is only accessible if:
        # 1. User is authenticated
        # 2. Tenant has can_use_calendar_sync enabled
        ...
```

## Usage Examples

### 1. Enable Calendar Sync for a Tenant

```python
# Via Django shell or management command
from core.models import Tenant

tenant = Tenant.objects.get(schema_name='demo')
tenant.can_use_calendar_sync = True
tenant.save()

# Check if tenant has feature
if tenant.has_feature('can_use_calendar_sync'):
    print("Calendar sync enabled!")
```

### 2. API Endpoints

#### Initiate Google Calendar OAuth

```bash
POST /api/oauth/google/initiate/
Content-Type: application/json

{
  "purpose": "calendar"
}

# Response (if permission granted):
{
  "success": true,
  "authorization_url": "https://accounts.google.com/o/oauth2/auth?..."
}

# Response (if permission denied):
{
  "success": false,
  "error": "Your current plan does not include Calendar Sync. Please upgrade..."
}
```

#### List Connected Calendars

```bash
GET /api/calendar/list/

# Response:
{
  "success": true,
  "calendars": [
    {
      "id": 1,
      "provider": "Google",
      "email": "user@gmail.com",
      "is_valid": true,
      "is_expired": false,
      "last_used_at": "2025-12-02T10:30:00Z",
      "created_at": "2025-12-01T08:15:00Z"
    }
  ]
}
```

#### Sync Calendar Events

```bash
POST /api/calendar/sync/
Content-Type: application/json

{
  "credential_id": 1,
  "calendar_id": "primary",
  "start_date": "2025-12-01",
  "end_date": "2025-12-31"
}

# Response:
{
  "success": true,
  "message": "Calendar sync started for user@gmail.com"
}
```

#### Check Calendar Sync Status

```bash
GET /api/calendar/status/

# Response (if feature enabled):
{
  "success": true,
  "can_use_calendar_sync": true,
  "total_connected": 2,
  "feature_enabled": true
}

# Response (if feature not enabled):
{
  "success": true,
  "can_use_calendar_sync": false,
  "message": "Calendar Sync feature is not available for your plan",
  "total_connected": 0
}
```

## Permission Flow Diagram

```
User Request to Calendar Endpoint
    ↓
[Is User Authenticated?]
    ├─ NO → 401 Unauthorized
    └─ YES ↓
[Is Request in Tenant Context?]
    ├─ NO → 400 Bad Request
    └─ YES ↓
[Does Tenant have can_use_calendar_sync?]
    ├─ NO → 403 Permission Denied (upgrade message)
    └─ YES ↓
[Process Request]
    ├─ Success → 200 OK
    └─ Error → 500 Server Error
```

## Integration with ViewSets

For ModelViewSet endpoints (like scheduling events from calendar sync):

```python
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from core.permissions import HasFeaturePermission
from schedule.models import Event

class EventViewSet(viewsets.ModelViewSet):
    queryset = Event.objects.all()
    serializer_class = EventSerializer

    # Permission for create/update/delete operations
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_use_calendar_sync')]

    def get_queryset(self):
        # Only show events that user's tenant can access
        return Event.objects.filter(tenant=self.request.tenant)

    @action(detail=False, methods=['post'])
    def sync_from_calendar(self, request):
        """Create events from a calendar sync"""
        # This action is protected by the permission_classes
        calendar_id = request.data.get('calendar_id')
        # ... implement sync logic ...
```

Note: If you only want to gate specific actions, override `get_permissions()`:

```python
def get_permissions(self):
    if self.action in ['create', 'update', 'destroy', 'sync_from_calendar']:
        # Only allow these actions if calendar sync is enabled
        return [IsAuthenticated(), HasFeaturePermission('can_use_calendar_sync')()]

    # Read-only actions don't require calendar sync permission
    return [IsAuthenticated()]
```

## Migration

The migration `0016_tenant_can_use_calendar_sync.py` adds the field to existing tenants with default value of `False`.

To apply the migration:

```bash
cd /home/poduck/Desktop/smoothschedule2/smoothschedule
docker compose -f docker-compose.local.yml exec django python manage.py migrate
```

## Testing

### Test Permission Denied

```python
from django.test import TestCase, RequestFactory
from rest_framework.test import APITestCase
from core.models import Tenant
from smoothschedule.identity.users.models import User

class CalendarSyncTests(APITestCase):
    def setUp(self):
        self.tenant = Tenant.objects.create(
            schema_name='test',
            name='Test Tenant',
            can_use_calendar_sync=False  # Feature disabled
        )
        self.user = User.objects.create_user(
            email='user@test.com',
            password='testpass',
            tenant=self.tenant
        )
        self.client.force_authenticate(user=self.user)

    def test_calendar_sync_permission_denied(self):
        """Test that users without permission cannot access calendar sync"""
        response = self.client.post('/api/calendar/list/')

        # Should return 403 Forbidden with upgrade message
        self.assertEqual(response.status_code, 403)
        self.assertIn('upgrade', response.json()['error'].lower())

    def test_calendar_sync_permission_granted(self):
        """Test that users with permission can access calendar sync"""
        self.tenant.can_use_calendar_sync = True
        self.tenant.save()

        response = self.client.get('/api/calendar/list/')

        # Should return 200 OK
        self.assertEqual(response.status_code, 200)
```

## Security Considerations

1. **Permission is checked at view level**: The `CalendarSyncPermission` checks that the tenant has the feature enabled
2. **Tenant isolation**: OAuthCredential queries filter by tenant to ensure data isolation
3. **Token security**: Tokens are stored encrypted at rest (configured in settings)
4. **CSRF protection**: OAuth state parameter prevents CSRF attacks
5. **Audit logging**: All calendar sync operations are logged with tenant and user information

## Related Files

- Model: `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/models.py` (Tenant.can_use_calendar_sync)
- Permission: `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/permissions.py` (HasFeaturePermission)
- OAuth Views: `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/oauth_views.py`
- Calendar Views: `/home/poduck/Desktop/smoothschedule2/smoothschedule/schedule/calendar_sync_views.py`
- Migration: `/home/poduck/Desktop/smoothschedule2/smoothschedule/core/migrations/0016_tenant_can_use_calendar_sync.py`
