<?php

namespace App\Services\MicrosoftGraph;

use App\Models\MsgraphPendingChange;
use App\Models\MsgraphModelChange;
use App\Models\Committee;
use App\Models\WorkGroup;
use App\Models\Contact;
use App\Models\ContactFolder;
use App\Models\User;
use App\Models\MicrosoftUser;
use App\Services\MicrosoftGraph\MicrosoftGraphUtilities;
use App\Services\MicrosoftGraph\MicrosoftGraphApiClient;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;

/**
 * Smart Sync Service - processes specific changes from tracking tables
 * Reads msgraph_pending_changes and msgraph_model_changes and applies exact operations
 */
class SmartSyncService
{
    protected $apiClient;

    public function __construct()
    {
        $this->apiClient = new MicrosoftGraphApiClient();
        $this->apiClient->initialize();
    }    /**
     * Process all pending changes
     */
    public function processAllPendingChanges(bool $dryRun = false): array
    {
        $results = [
            'membership_changes' => 0,
            'model_changes' => 0,
            'optimized_away' => 0,
            'errors' => []
        ];

        // First, optimize pending changes to remove redundant operations
        $optimizedCount = $this->optimizePendingChanges($dryRun);
        $results['optimized_away'] = $optimizedCount;

        // Process membership changes - specific actions
        $membershipChanges = MsgraphPendingChange::where('processed', false)->get();
        foreach ($membershipChanges as $change) {
            try {
                $this->processMembershipChange($change, $dryRun);
                $results['membership_changes']++;
            } catch (\Exception $e) {
                $results['errors'][] = "Membership change {$change->id}: " . $e->getMessage();
                Log::error("Error processing membership change {$change->id}", ['error' => $e->getMessage()]);
            }
        }

        // Process model changes - specific actions
        $modelChanges = MsgraphModelChange::where('processed', false)->get();
        foreach ($modelChanges as $change) {
            try {
                $this->processModelChange($change, $dryRun);
                $results['model_changes']++;
            } catch (\Exception $e) {
                $results['errors'][] = "Model change {$change->id}: " . $e->getMessage();
                Log::error("Error processing model change {$change->id}", ['error' => $e->getMessage()]);
            }
        }

        return $results;
    }

    /**
     * Optimize pending changes by removing redundant operations
     * For example: user_joined followed by user_left = no operations needed
     */
    public function optimizePendingChanges(bool $dryRun = false): int
    {
        Log::info("Starting optimization of pending changes");

        $membershipChanges = MsgraphPendingChange::where('processed', false)
            ->orderBy('created_at')
            ->get();

        $optimizedCount = 0;

        // Group changes by user and model for easier analysis
        $changesByUserModel = [];
        foreach ($membershipChanges as $change) {
            $key = "{$change->user_id}_{$change->model_type}_{$change->model_id}";
            if (!isset($changesByUserModel[$key])) {
                $changesByUserModel[$key] = [];
            }
            $changesByUserModel[$key][] = $change;
        }

        // Analyze each group for optimization opportunities
        foreach ($changesByUserModel as $key => $changes) {
            if (count($changes) < 2) continue;

            Log::info("Analyzing changes for optimization", [
                'user_model_key' => $key,
                'changes_count' => count($changes),
                'changes' => array_map(function($c) {
                    return [
                        'id' => $c->id,
                        'action' => $c->action,
                        'old_type' => $c->old_membership_type,
                        'new_type' => $c->new_membership_type,
                        'created_at' => $c->created_at
                    ];
                }, $changes)
            ]);

            $optimized = $this->optimizeChangeGroup($changes, $dryRun);
            $optimizedCount += $optimized;
        }

        Log::info("Completed optimization of pending changes", [
            'optimized_count' => $optimizedCount
        ]);

        return $optimizedCount;
    }

    /**
     * Optimize a group of changes for the same user/model combination
     */
    private function optimizeChangeGroup(array $changes, bool $dryRun): int
    {
        $optimizedCount = 0;
        $changesToMark = [];

        // Sort by creation time to process in chronological order
        usort($changes, function($a, $b) {
            return $a->created_at <=> $b->created_at;
        });

        // Look for specific optimization patterns
        for ($i = 0; $i < count($changes) - 1; $i++) {
            $current = $changes[$i];
            $next = $changes[$i + 1];

            // Skip if either change is already marked for removal
            if (in_array($current->id, $changesToMark) || in_array($next->id, $changesToMark)) {
                continue;
            }

            // Pattern 1: user_joined followed by user_left = cancel both
            if ($current->action === 'user_joined' && $next->action === 'user_left') {
                $changesToMark[] = $current->id;
                $changesToMark[] = $next->id;
                $optimizedCount += 2;

                Log::info("Optimized: user_joined + user_left = cancelled", [
                    'joined_change_id' => $current->id,
                    'left_change_id' => $next->id,
                    'user_id' => $current->user_id,
                    'model_type' => $current->model_type,
                    'model_id' => $current->model_id
                ]);
            }
            // Pattern 2: user_left followed by user_joined = cancel both
            elseif ($current->action === 'user_left' && $next->action === 'user_joined') {
                $changesToMark[] = $current->id;
                $changesToMark[] = $next->id;
                $optimizedCount += 2;

                Log::info("Optimized: user_left + user_joined = cancelled", [
                    'left_change_id' => $current->id,
                    'joined_change_id' => $next->id,
                    'user_id' => $current->user_id,
                    'model_type' => $current->model_type,
                    'model_id' => $current->model_id
                ]);
            }
            // Pattern 3: Multiple user_type_changed = keep only the last one
            elseif ($current->action === 'user_type_changed' && $next->action === 'user_type_changed') {
                $changesToMark[] = $current->id;
                $optimizedCount += 1;

                Log::info("Optimized: redundant user_type_changed removed", [
                    'removed_change_id' => $current->id,
                    'kept_change_id' => $next->id,
                    'user_id' => $current->user_id,
                    'model_type' => $current->model_type,
                    'model_id' => $current->model_id,
                    'old_type_removed' => $current->old_membership_type,
                    'new_type_removed' => $current->new_membership_type,
                    'old_type_kept' => $next->old_membership_type,
                    'new_type_kept' => $next->new_membership_type
                ]);

                // Update the next change to use the original old_membership_type
                if (!$dryRun) {
                    $next->update([
                        'old_membership_type' => $current->old_membership_type
                    ]);
                }
            }
            // Pattern 4: user_joined + user_type_changed = optimize to single user_joined with final type
            elseif ($current->action === 'user_joined' && $next->action === 'user_type_changed') {
                $changesToMark[] = $current->id;
                $changesToMark[] = $next->id;

                // Create a new optimized user_joined change with the final membership type
                if (!$dryRun) {
                    MsgraphPendingChange::create([
                        'user_id' => $current->user_id,
                        'model_type' => $current->model_type,
                        'model_id' => $current->model_id,
                        'action' => 'user_joined',
                        'old_membership_type' => null,
                        'new_membership_type' => $next->new_membership_type,
                        'metadata' => array_merge($current->metadata ?? [], ['optimized_from' => [$current->id, $next->id]])
                    ]);
                }

                $optimizedCount += 2;

                Log::info("Optimized: user_joined + user_type_changed = single user_joined", [
                    'original_joined_id' => $current->id,
                    'original_type_changed_id' => $next->id,
                    'user_id' => $current->user_id,
                    'model_type' => $current->model_type,
                    'model_id' => $current->model_id,
                    'initial_type' => $current->new_membership_type,
                    'final_type' => $next->new_membership_type
                ]);
            }
            // Pattern 5: user_type_changed + user_left = optimize to single user_left
            elseif ($current->action === 'user_type_changed' && $next->action === 'user_left') {
                $changesToMark[] = $current->id;
                $optimizedCount += 1;

                Log::info("Optimized: user_type_changed + user_left = keep only user_left", [
                    'removed_type_changed_id' => $current->id,
                    'kept_user_left_id' => $next->id,
                    'user_id' => $current->user_id,
                    'model_type' => $current->model_type,
                    'model_id' => $current->model_id
                ]);
            }
            // Pattern 6: user_state_changed with opposing states = cancel both if they return to original state
            elseif ($current->action === 'user_state_changed' && $next->action === 'user_state_changed') {
                $currentOldState = $current->metadata['old_state'] ?? null;
                $currentNewState = $current->metadata['new_state'] ?? null;
                $nextOldState = $next->metadata['old_state'] ?? null;
                $nextNewState = $next->metadata['new_state'] ?? null;

                // If the states return to the original (e.g., active -> inactive -> active), cancel both
                if ($currentOldState && $nextNewState && $currentOldState === $nextNewState) {
                    $changesToMark[] = $current->id;
                    $changesToMark[] = $next->id;
                    $optimizedCount += 2;

                    Log::info("Optimized: user_state_changed cycle cancelled", [
                        'first_change_id' => $current->id,
                        'second_change_id' => $next->id,
                        'user_id' => $current->user_id,
                        'model_type' => $current->model_type,
                        'model_id' => $current->model_id,
                        'cycle' => "{$currentOldState} -> {$currentNewState} -> {$nextNewState}"
                    ]);
                } else {
                    // Multiple state changes - keep only the last one with correct original state
                    $changesToMark[] = $current->id;
                    $optimizedCount += 1;

                    // Update the next change to use the original old_state
                    if (!$dryRun && $currentOldState) {
                        $nextMetadata = $next->metadata ?? [];
                        $nextMetadata['old_state'] = $currentOldState;
                        $next->update(['metadata' => $nextMetadata]);
                    }

                    Log::info("Optimized: redundant user_state_changed removed", [
                        'removed_change_id' => $current->id,
                        'kept_change_id' => $next->id,
                        'user_id' => $current->user_id,
                        'model_type' => $current->model_type,
                        'model_id' => $current->model_id,
                        'original_old_state' => $currentOldState,
                        'final_new_state' => $nextNewState
                    ]);
                }
            }
        }

        // Mark optimized changes as processed without calling Microsoft Graph
        if (!$dryRun && !empty($changesToMark)) {
            MsgraphPendingChange::whereIn('id', $changesToMark)->update([
                'processed' => true,
                'processed_at' => now(),
                'metadata' => DB::raw("JSON_SET(COALESCE(metadata, '{}'), '$.optimized_away', true, '$.optimized_at', '" . now()->toISOString() . "')")
            ]);

            Log::info("Marked optimized changes as processed", [
                'marked_count' => count($changesToMark),
                'change_ids' => $changesToMark
            ]);
        }

        return $optimizedCount;
    }

    /**
     * Process a single membership change based on specific action
     */
    public function processMembershipChange(MsgraphPendingChange $change, bool $dryRun = false): void
    {
        Log::info("Processing membership change: {$change->action} for user {$change->user_id} in {$change->model_type} {$change->model_id}", [
            'change_id' => $change->id,
            'action' => $change->action,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'user_id' => $change->user_id,
            'old_membership_type' => $change->old_membership_type,
            'new_membership_type' => $change->new_membership_type
        ]);

        if (!$dryRun) {
            switch ($change->action) {
                case 'user_joined':
                    $this->handleUserJoinedFromData($change);
                    break;

                case 'user_left':
                    $this->handleUserLeftFromData($change);
                    break;

                case 'user_type_changed':
                    $this->handleUserTypeChangedFromData($change);
                    break;

                case 'user_deleted':
                    $this->handleUserDeletedFromData($change);
                    break;

                case 'user_state_changed':
                    $this->handleUserStateChangedFromData($change);
                    break;
            }

            // Mark as processed
            $change->update([
                'processed' => true,
                'processed_at' => now()
            ]);
        }
    }

    /**
     * Process a single model change based on specific action
     */
    public function processModelChange(MsgraphModelChange $change, bool $dryRun = false): void
    {
        Log::info("Processing model change: {$change->action} for {$change->model_type} {$change->model_id}", [
            'change_id' => $change->id,
            'action' => $change->action,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'model_exists' => $change->model ? 'yes' : 'no'
        ]);

        if (!$dryRun) {
            switch ($change->action) {
                case 'model_created':
                    $this->handleModelCreated($change);
                    break;

                case 'model_deleted':
                    $this->handleModelDeleted($change);
                    break;

                case 'status_changed':
                    $this->handleStatusChangeFromData($change);
                    break;

                case 'title_changed':
                    $this->handleTitleChangeFromData($change);
                    break;
            }

            // Mark as processed
            $change->update([
                'processed' => true,
                'processed_at' => now()
            ]);
        }
    }

    /**
     * Get counts of pending changes
     */
    public function getPendingChangeCounts(): array
    {
        return [
            'membership_changes' => MsgraphPendingChange::where('processed', false)->count(),
            'model_changes' => MsgraphModelChange::where('processed', false)->count()
        ];
    }

    /**
     * Clean up old processed changes (older than 30 days)
     */
    public function cleanupOldChanges(): int
    {
        $cutoff = now()->subDays(30);
        $deleted = 0;

        $deleted += MsgraphPendingChange::where('processed', true)
            ->where('processed_at', '<', $cutoff)
            ->delete();

        $deleted += MsgraphModelChange::where('processed', true)
            ->where('processed_at', '<', $cutoff)
            ->delete();

        return $deleted;
    }

    /**
     * Add user to contact folders (when user joins)
     */
    private function addUserToContactFolders($model, User $user, ?string $membershipType): void
    {
        $contactFolders = $model->contactFolders()->whereNotNull('ms_folder_id')->get();
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        Log::info("Adding user to contact folders", [
            'user_id' => $user->id,
            'user_email' => $user->email,
            'model_type' => get_class($model),
            'model_id' => $model->id,
            'model_title' => $model->title ?? 'N/A',
            'membership_type' => $membershipType,
            'total_contact_folders' => $contactFolders->count(),
            'service_users_count' => $serviceUsers->count()
        ]);

        foreach ($serviceUsers as $serviceUser) {
            // Get folders specifically for this service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            Log::info("Processing folders for service user", [
                'service_user_id' => $serviceUser->id,
                'service_user_email' => $serviceUser->email,
                'user_folders_count' => $userFolders->count(),
                'user_folders' => $userFolders->map(function($folder) {
                    return [
                        'id' => $folder->id,
                        'ms_folder_id' => $folder->ms_folder_id,
                        'display_name' => $folder->display_name,
                        'folder_type' => strtolower($this->getFolderType($folder))
                    ];
                })->toArray()
            ]);

            foreach ($userFolders as $contactFolder) {
                // Use the actual membership type from the pivot table
                // membershipType should be "to" or "cc"
                $folderType = strtolower($this->getFolderType($contactFolder));

                Log::info("Checking folder match", [
                    'folder_type' => $folderType,
                    'membership_type' => $membershipType,
                    'folder_name' => $contactFolder->display_name,
                    'match' => $folderType === $membershipType
                ]);

                if ($folderType === $membershipType) {
                    $this->addContactToFolder($contactFolder->ms_folder_id, $user, $serviceUser);
                    Log::info("Added user {$user->id} to {$folderType} folder for {$model->title} in {$serviceUser->email}");
                }
            }
        }
    }

    /**
     * Remove user from all contact folders (when user leaves)
     */
    private function removeUserFromContactFolders($model, User $user): void
    {
        $contactFolders = $model->contactFolders()->whereNotNull('ms_folder_id')->get();
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        foreach ($serviceUsers as $serviceUser) {
            // Get folders specifically for this service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            foreach ($userFolders as $contactFolder) {
                $this->removeContactFromFolder($contactFolder->ms_folder_id, $user, $serviceUser);
                $folderType = strtolower($this->getFolderType($contactFolder));
                Log::info("Removed user {$user->id} from {$folderType} folder for {$model->title} in {$serviceUser->email}");
            }
        }
    }

    /**
     * Change user membership type (e.g., from CC to TO or vice versa)
     * This is the key functionality you mentioned
     */
    private function changeUserMembershipType($model, User $user, ?string $oldType, ?string $newType): void
    {
        $contactFolders = $model->contactFolders()->whereNotNull('ms_folder_id')->get();
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        // Use the actual membership types from the pivot table
        // oldType and newType should be "to" or "cc"
        foreach ($serviceUsers as $serviceUser) {
            // Get folders specifically for this service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            $toFolder = $userFolders->filter(function($folder) {
                return strtolower($this->getFolderType($folder)) === 'to';
            })->first();

            $ccFolder = $userFolders->filter(function($folder) {
                return strtolower($this->getFolderType($folder)) === 'cc';
            })->first();

            // If changed from CC to TO
            if ($oldType === 'cc' && $newType === 'to') {
                if ($ccFolder) {
                    $this->removeContactFromFolder($ccFolder->ms_folder_id, $user, $serviceUser);
                    Log::info("Removed user {$user->id} from CC folder for {$model->title} in {$serviceUser->email}");
                }
                if ($toFolder) {
                    $this->addContactToFolder($toFolder->ms_folder_id, $user, $serviceUser);
                    Log::info("Added user {$user->id} to TO folder for {$model->title} in {$serviceUser->email}");
                }
            }
            // If changed from TO to CC
            elseif ($oldType === 'to' && $newType === 'cc') {
                if ($toFolder) {
                    $this->removeContactFromFolder($toFolder->ms_folder_id, $user, $serviceUser);
                    Log::info("Removed user {$user->id} from TO folder for {$model->title} in {$serviceUser->email}");
                }
                if ($ccFolder) {
                    $this->addContactToFolder($ccFolder->ms_folder_id, $user, $serviceUser);
                    Log::info("Added user {$user->id} to CC folder for {$model->title} in {$serviceUser->email}");
                }
            }
        }
    }

    /**
     * Create contact folders for a new model
     */
    private function createContactFoldersForModel($model): void
    {
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        $folderTypes = ['to', 'cc'];

        foreach ($serviceUsers as $serviceUser) {
            foreach ($folderTypes as $type) {
                $folderType = strtoupper($type);
                $modelType = ($model instanceof Committee) ? 'committee' : 'workgroup';

                $displayName = MicrosoftGraphUtilities::generateFolderDisplayName($model, $modelType, $folderType);
                $sanitizedDisplayName = MicrosoftGraphUtilities::sanitizeFolderDisplayName($displayName);

                // Create folder in Microsoft Graph
                $response = $this->apiClient->postWithRetry("users/{$serviceUser->email}/contactFolders", [
                    'displayName' => $sanitizedDisplayName
                ]);

                if (isset($response['id'])) {
                    // Create local ContactFolder record
                    ContactFolder::create([
                        'folderable_type' => get_class($model),
                        'folderable_id' => $model->id,
                        'ms_folder_id' => $response['id'],
                        'display_name' => $sanitizedDisplayName,
                        'microsoft_user_id' => $serviceUser->id
                    ]);

                    Log::info("Created {$type} folder for {$model->title}: {$sanitizedDisplayName} in {$serviceUser->email}");
                }
            }
        }
    }

    /**
     * Delete contact folders for a deleted model
     */
    private function deleteContactFoldersForModel($model): void
    {
        Log::info("Starting deleteContactFoldersForModel for model", [
            'model_type' => get_class($model),
            'model_id' => $model->id,
            'model_title' => $model->title ?? 'N/A'
        ]);

        $contactFolders = $model->contactFolders()->whereNotNull('ms_folder_id')->get();
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        Log::info("Found data for deletion", [
            'contact_folders_count' => $contactFolders->count(),
            'service_users_count' => $serviceUsers->count(),
            'contact_folders' => $contactFolders->map(function($folder) {
                return [
                    'id' => $folder->id,
                    'ms_folder_id' => $folder->ms_folder_id,
                    'display_name' => $folder->display_name,
                    'microsoft_user_id' => $folder->microsoft_user_id
                ];
            })->toArray()
        ]);

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        if ($contactFolders->isEmpty()) {
            Log::warning("No contact folders found for model", [
                'model_type' => get_class($model),
                'model_id' => $model->id
            ]);
            return;
        }

        foreach ($serviceUsers as $serviceUser) {
            // Get folders for this specific service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            Log::info("Processing folders for service user", [
                'service_user_id' => $serviceUser->id,
                'service_user_email' => $serviceUser->email,
                'user_folders_count' => $userFolders->count(),
                'user_folders' => $userFolders->map(function($folder) {
                    return [
                        'id' => $folder->id,
                        'ms_folder_id' => $folder->ms_folder_id,
                        'display_name' => $folder->display_name
                    ];
                })->toArray()
            ]);

            foreach ($userFolders as $contactFolder) {
                try {
                    Log::info("Attempting to delete folder from Microsoft Graph", [
                        'folder_id' => $contactFolder->id,
                        'ms_folder_id' => $contactFolder->ms_folder_id,
                        'display_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email
                    ]);

                    $this->apiClient->postWithRetry("users/{$serviceUser->email}/contactFolders/{$contactFolder->ms_folder_id}/permanentDelete", []);
                    Log::info("Successfully deleted folder from Microsoft Graph", [
                        'folder_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email
                    ]);

                    // Delete associated contacts first
                    $deletedContacts = Contact::where('contact_folder_id', $contactFolder->id)->count();
                    Contact::where('contact_folder_id', $contactFolder->id)->delete();
                    Log::info("Deleted associated contacts", [
                        'contact_folder_id' => $contactFolder->id,
                        'deleted_contacts_count' => $deletedContacts
                    ]);

                    // Delete local folder record
                    $contactFolder->delete();
                    Log::info("Deleted local folder record", [
                        'folder_id' => $contactFolder->id,
                        'display_name' => $contactFolder->display_name
                    ]);

                } catch (\Exception $e) {
                    Log::error("Failed to delete folder", [
                        'folder_id' => $contactFolder->id,
                        'ms_folder_id' => $contactFolder->ms_folder_id,
                        'display_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email,
                        'error' => $e->getMessage(),
                        'trace' => $e->getTraceAsString()
                    ]);
                    // Continue with other folders even if one fails
                }
            }
        }

        Log::info("Completed deleteContactFoldersForModel", [
            'model_type' => get_class($model),
            'model_id' => $model->id
        ]);
    }

    /**
     * Handle status changes (published/unpublished)
     */
    private function handleStatusChange($model, ?string $oldStatus, ?string $newStatus): void
    {
        // If changed from unpublished to published, create folders
        if ($oldStatus !== 'published' && $newStatus === 'published') {
            $this->createContactFoldersForModel($model);
        }
        // If changed from published to unpublished, delete folders
        elseif ($oldStatus === 'published' && $newStatus !== 'published') {
            $this->deleteContactFoldersForModel($model);
        }
    }

    /**
     * Update contact folder titles when model title changes
     */
    private function updateContactFolderTitles($model, ?string $oldTitle, ?string $newTitle, ?string $language): void
    {
        $contactFolders = $model->contactFolders()->whereNotNull('ms_folder_id')->get();
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        foreach ($serviceUsers as $serviceUser) {
            // Get folders for this specific service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            foreach ($userFolders as $contactFolder) {
                // Get folder type from display name
                $folderType = strtolower($this->getFolderType($contactFolder));
                $newFolderName = $this->generateFolderName($model, $folderType);

                try {
                    $this->apiClient->patchWithRetry("users/{$serviceUser->email}/contactFolders/{$contactFolder->ms_folder_id}", [
                        'displayName' => $newFolderName
                    ]);

                    // Update local record
                    $contactFolder->update(['display_name' => $newFolderName]);

                    Log::info("Updated folder name from '{$contactFolder->display_name}' to '{$newFolderName}' for {$serviceUser->email}");
                } catch (\Exception $e) {
                    Log::warning("Failed to update folder name for {$serviceUser->email}: " . $e->getMessage());
                }
            }
        }
    }

    /**
     * Add a contact to a specific folder
     */
    private function addContactToFolder(string $folderId, User $user, MicrosoftUser $serviceUser): void
    {
        Log::info("Attempting to add contact to folder", [
            'user_email' => $user->email,
            'user_id' => $user->id,
            'ms_folder_id' => $folderId,
            'service_user_id' => $serviceUser->id,
            'service_user_email' => $serviceUser->email
        ]);

        // First, get the local ContactFolder to associate the contact with
        $contactFolder = ContactFolder::where('ms_folder_id', $folderId)
            ->where('microsoft_user_id', $serviceUser->id)
            ->first();

        if (!$contactFolder) {
            // Add detailed logging for debugging
            $allFoldersWithThisId = ContactFolder::where('ms_folder_id', $folderId)->get();
            $allFoldersForUser = ContactFolder::where('microsoft_user_id', $serviceUser->id)->get();

            Log::error("Contact folder not found for ms_folder_id", [
                'ms_folder_id' => $folderId,
                'service_user_id' => $serviceUser->id,
                'service_user_email' => $serviceUser->email,
                'folders_with_this_id' => $allFoldersWithThisId->map(function($f) {
                    return [
                        'id' => $f->id,
                        'display_name' => $f->display_name,
                        'microsoft_user_id' => $f->microsoft_user_id,
                        'folderable_type' => $f->folderable_type,
                        'folderable_id' => $f->folderable_id
                    ];
                })->toArray(),
                'folders_for_service_user' => $allFoldersForUser->map(function($f) {
                    return [
                        'id' => $f->id,
                        'ms_folder_id' => $f->ms_folder_id,
                        'display_name' => $f->display_name,
                        'folderable_type' => $f->folderable_type,
                        'folderable_id' => $f->folderable_id
                    ];
                })->toArray()
            ]);

            throw new \Exception("Contact folder not found for ms_folder_id: {$folderId}");
        }

        // Check if contact already exists in this folder
        $existingContact = Contact::where('contact_folder_id', $contactFolder->id)
            ->where('email', $user->email)
            ->first();

        if ($existingContact) {
            Log::info("Contact already exists for user {$user->email} in folder {$contactFolder->display_name}");
            return;
        }

        $contactData = [
            'displayName' => $user->name,
            'emailAddresses' => [
                [
                    'address' => $user->email,
                    'name' => $user->name
                ]
            ]
        ];

        // Create contact in Microsoft Graph
        $response = $this->apiClient->postWithRetry("users/{$serviceUser->email}/contactFolders/{$folderId}/contacts", $contactData);
        Log::info("Microsoft Graph response for adding contact", [
            'response' => $response
        ]);
        if (isset($response['id'])) {
            // Save contact locally for faster future operations
            Contact::create([
                'ms_contact_id' => $response['id'],
                'contact_folder_id' => $contactFolder->id,
                'display_name' => $user->name,
                'email' => $user->email,
                'given_name' => $user->first_name ?? '',
                'surname' => $user->last_name ?? '',
            ]);

            Log::info("Created and saved contact for user {$user->email} in folder {$contactFolder->display_name}");
        }
    }

    /**
     * Remove a contact from a specific folder
     */
    private function removeContactFromFolder(string $folderId, User $user, MicrosoftUser $serviceUser): void
    {
        // Find the local ContactFolder first
        $contactFolder = ContactFolder::where('ms_folder_id', $folderId)
            ->where('microsoft_user_id', $serviceUser->id)
            ->first();

        if (!$contactFolder) {
            Log::warning("Contact folder not found for ms_folder_id: {$folderId}");
            return;
        }

        // Find the contact in our local database
        $contact = Contact::where('contact_folder_id', $contactFolder->id)
            ->where('email', $user->email)
            ->first();

        if (!$contact) {
            Log::info("Contact not found locally for user {$user->email} in folder {$contactFolder->display_name}");
            return;
        }

        try {
            // Delete from Microsoft Graph using the stored ms_contact_id
            $this->apiClient->deleteWithRetry("users/{$serviceUser->email}/contacts/{$contact->ms_contact_id}");

            // Delete from local database
            $contact->delete();

            Log::info("Removed contact for user {$user->email} from folder {$contactFolder->display_name}");
        } catch (\Exception $e) {
            Log::error("Failed to remove contact {$contact->ms_contact_id}: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Generate folder name based on model and type using the same logic as the full sync
     */
    private function generateFolderName($model, string $type): string
    {
        $modelType = ($model instanceof Committee) ? 'committee' : 'workgroup';
        $folderType = strtoupper($type);

        $displayName = MicrosoftGraphUtilities::generateFolderDisplayName($model, $modelType, $folderType);
        return MicrosoftGraphUtilities::sanitizeFolderDisplayName($displayName);
    }

    /**
     * Get the folder type (TO/CC) from the display name
     */
    private function getFolderType(ContactFolder $contactFolder): string
    {
        return MicrosoftGraphUtilities::extractFolderType($contactFolder->display_name);
    }
    private function getCurrentMembershipType($model, User $user): ?string
    {
        if ($model instanceof Committee) {
            $membership = $user->committees()
                ->withPivot('type')
                ->where('committee_id', $model->id)
                ->first();
            return $membership ? $membership->pivot->type : null;
        } elseif ($model instanceof WorkGroup) {
            $membership = $user->workGroups()
                ->withPivot('type')
                ->where('work_group_id', $model->id)
                ->first();
            return $membership ? $membership->pivot->type : null;
        }

        return null;
    }

    /**
     * Handle model created using only change data
     */
    private function handleModelCreated(MsgraphModelChange $change): void
    {
        // If the model still exists, use it. Otherwise, skip since it was probably deleted later
        $model = $change->model;
        if ($model) {
            Log::info("Model still exists, creating folders for {$change->model_type} {$change->model_id}");
            $this->createContactFoldersForModel($model);
        } else {
            Log::warning("Model {$change->model_type} {$change->model_id} no longer exists, skipping creation", [
                'change_id' => $change->id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
        }
    }

    /**
     * Handle model deleted using only change data - this is the key fix
     */
    private function handleModelDeleted(MsgraphModelChange $change): void
    {
        Log::info("Handling model deletion for {$change->model_type} {$change->model_id} using stored data", [
            'change_id' => $change->id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id
        ]);

        // Find contact folders by model type and ID directly from database
        $modelClass = $this->getModelClassFromType($change->model_type);
        $contactFolders = ContactFolder::where('folderable_type', $modelClass)
            ->where('folderable_id', $change->model_id)
            ->whereNotNull('ms_folder_id')
            ->get();

        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        Log::info("Found data for deletion using change data", [
            'contact_folders_count' => $contactFolders->count(),
            'service_users_count' => $serviceUsers->count(),
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'contact_folders' => $contactFolders->map(function($folder) {
                return [
                    'id' => $folder->id,
                    'ms_folder_id' => $folder->ms_folder_id,
                    'display_name' => $folder->display_name,
                    'microsoft_user_id' => $folder->microsoft_user_id
                ];
            })->toArray()
        ]);

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        if ($contactFolders->isEmpty()) {
            Log::warning("No contact folders found for deleted model", [
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
            return;
        }

        foreach ($serviceUsers as $serviceUser) {
            // Get folders for this specific service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            Log::info("Processing folders for service user in model deletion", [
                'service_user_id' => $serviceUser->id,
                'service_user_email' => $serviceUser->email,
                'user_folders_count' => $userFolders->count(),
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);

            foreach ($userFolders as $contactFolder) {
                try {
                    Log::info("Attempting to delete folder from Microsoft Graph (model deleted)", [
                        'folder_id' => $contactFolder->id,
                        'ms_folder_id' => $contactFolder->ms_folder_id,
                        'display_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email,
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id
                    ]);

                    $this->apiClient->postWithRetry("users/{$serviceUser->email}/contactFolders/{$contactFolder->ms_folder_id}/permanentDelete", []);
                    Log::info("Successfully deleted folder from Microsoft Graph (model deleted)", [
                        'folder_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email,
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id
                    ]);

                    // Delete associated contacts first
                    $deletedContacts = Contact::where('contact_folder_id', $contactFolder->id)->count();
                    Contact::where('contact_folder_id', $contactFolder->id)->delete();
                    Log::info("Deleted associated contacts (model deleted)", [
                        'contact_folder_id' => $contactFolder->id,
                        'deleted_contacts_count' => $deletedContacts,
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id
                    ]);

                    // Delete local folder record
                    $contactFolder->delete();
                    Log::info("Deleted local folder record (model deleted)", [
                        'folder_id' => $contactFolder->id,
                        'display_name' => $contactFolder->display_name,
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id
                    ]);

                } catch (\Exception $e) {
                    Log::error("Failed to delete folder (model deleted)", [
                        'folder_id' => $contactFolder->id,
                        'ms_folder_id' => $contactFolder->ms_folder_id,
                        'display_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email,
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id,
                        'error' => $e->getMessage(),
                        'trace' => $e->getTraceAsString()
                    ]);
                    // Continue with other folders even if one fails
                }
            }
        }

        Log::info("Completed model deletion handling", [
            'model_type' => $change->model_type,
            'model_id' => $change->model_id
        ]);
    }

    /**
     * Handle status change using only change data
     */
    private function handleStatusChangeFromData(MsgraphModelChange $change): void
    {
        Log::info("Handling status change using change data", [
            'change_id' => $change->id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'old_status' => $change->old_status,
            'new_status' => $change->new_status
        ]);

        // If changed from unpublished to published, create folders (if model still exists)
        if ($change->old_status !== 'published' && $change->new_status === 'published') {
            $model = $change->model;
            if ($model) {
                Log::info("Creating folders due to status change to published");
                $this->createContactFoldersForModel($model);
            } else {
                Log::warning("Cannot create folders - model no longer exists", [
                    'model_type' => $change->model_type,
                    'model_id' => $change->model_id
                ]);
            }
        }
        // If changed from published to unpublished, delete folders
        elseif ($change->old_status === 'published' && $change->new_status !== 'published') {
            Log::info("Deleting folders due to status change from published");
            $this->deleteContactFoldersByModelData($change->model_type, $change->model_id);
        }
    }

    /**
     * Handle title change using only change data
     */
    private function handleTitleChangeFromData(MsgraphModelChange $change): void
    {
        Log::info("Handling title change using change data", [
            'change_id' => $change->id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'old_title' => $change->old_title,
            'new_title' => $change->new_title
        ]);

        // Find contact folders by model type and ID directly from database
        $modelClass = $this->getModelClassFromType($change->model_type);
        $contactFolders = ContactFolder::where('folderable_type', $modelClass)
            ->where('folderable_id', $change->model_id)
            ->whereNotNull('ms_folder_id')
            ->get();

        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        if ($contactFolders->isEmpty()) {
            Log::warning("No contact folders found for title change", [
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
            return;
        }

        // We need the actual model for generating the new folder names
        $model = $change->model;
        if (!$model) {
            Log::warning("Cannot update folder titles - model no longer exists", [
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
            return;
        }

        foreach ($serviceUsers as $serviceUser) {
            // Get folders for this specific service user
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            foreach ($userFolders as $contactFolder) {
                // Get folder type from display name
                $folderType = strtolower($this->getFolderType($contactFolder));
                $newFolderName = $this->generateFolderName($model, $folderType);

                try {
                    $this->apiClient->patchWithRetry("users/{$serviceUser->email}/contactFolders/{$contactFolder->ms_folder_id}", [
                        'displayName' => $newFolderName
                    ]);

                    // Update local record
                    $contactFolder->update(['display_name' => $newFolderName]);

                    Log::info("Updated folder name due to title change", [
                        'old_name' => $contactFolder->display_name,
                        'new_name' => $newFolderName,
                        'service_user_email' => $serviceUser->email,
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id
                    ]);
                } catch (\Exception $e) {
                    Log::error("Failed to update folder name", [
                        'service_user_email' => $serviceUser->email,
                        'folder_id' => $contactFolder->id,
                        'error' => $e->getMessage(),
                        'model_type' => $change->model_type,
                        'model_id' => $change->model_id
                    ]);
                }
            }
        }
    }

    /**
     * Delete contact folders by model data (when model might not exist)
     */
    private function deleteContactFoldersByModelData(string $modelType, int $modelId): void
    {
        $modelClass = $this->getModelClassFromType($modelType);
        $contactFolders = ContactFolder::where('folderable_type', $modelClass)
            ->where('folderable_id', $modelId)
            ->whereNotNull('ms_folder_id')
            ->get();

        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            throw new \Exception('No Microsoft service users found');
        }

        foreach ($serviceUsers as $serviceUser) {
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            foreach ($userFolders as $contactFolder) {
                try {
                    $this->apiClient->postWithRetry("users/{$serviceUser->email}/contactFolders/{$contactFolder->ms_folder_id}/permanentDelete", []);

                    // Delete associated contacts first
                    Contact::where('contact_folder_id', $contactFolder->id)->delete();

                    // Delete local folder record
                    $contactFolder->delete();

                    Log::info("Deleted folder by model data", [
                        'folder_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email,
                        'model_type' => $modelType,
                        'model_id' => $modelId
                    ]);
                } catch (\Exception $e) {
                    Log::error("Failed to delete folder by model data", [
                        'folder_id' => $contactFolder->id,
                        'service_user_email' => $serviceUser->email,
                        'error' => $e->getMessage(),
                        'model_type' => $modelType,
                        'model_id' => $modelId
                    ]);
                }
            }
        }
    }

    /**
     * Handle user joined using only change data
     */
    private function handleUserJoinedFromData(MsgraphPendingChange $change): void
    {
        Log::info("Handling user joined using change data", [
            'change_id' => $change->id,
            'user_id' => $change->user_id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'membership_type' => $change->new_membership_type
        ]);

        // Try to find the user and model
        $user = User::find($change->user_id);
        $model = $this->findModelByTypeAndId($change->model_type, $change->model_id);

        if (!$user) {
            Log::warning("Cannot process user_joined - user no longer exists", [
                'change_id' => $change->id,
                'user_id' => $change->user_id
            ]);
            return;
        }

        if (!$model) {
            Log::warning("Cannot process user_joined - model no longer exists", [
                'change_id' => $change->id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
            return;
        }

        // Use membership type from change, or get current one as fallback
        $membershipType = $change->new_membership_type ?: $this->getCurrentMembershipType($model, $user);

        if ($membershipType) {
            $this->addUserToContactFolders($model, $user, $membershipType);
            Log::info("Successfully processed user_joined", [
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id,
                'membership_type' => $membershipType
            ]);
        } else {
            Log::warning("Cannot determine membership type for user_joined", [
                'change_id' => $change->id,
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
        }
    }

    /**
     * Handle user left using only change data
     */
    private function handleUserLeftFromData(MsgraphPendingChange $change): void
    {
        Log::info("Handling user left using change data", [
            'change_id' => $change->id,
            'user_id' => $change->user_id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id
        ]);

        // For user_left, we can process even if user is deleted by finding contacts by user data
        $user = User::find($change->user_id);

        if ($user) {
            // User still exists, use normal flow
            $model = $this->findModelByTypeAndId($change->model_type, $change->model_id);
            if ($model) {
                $this->removeUserFromContactFolders($model, $user);
                Log::info("Successfully processed user_left with existing user and model");
            } else {
                // Model deleted, remove user from folders by model data
                $this->removeUserFromContactFoldersByModelData($change->user_id, $change->model_type, $change->model_id, $user);
                Log::info("Successfully processed user_left with existing user but deleted model");
            }
        } else {
            // User deleted, remove contacts by user ID and model data
            $this->removeUserFromContactFoldersByUserData($change->user_id, $change->model_type, $change->model_id);
            Log::info("Successfully processed user_left with deleted user", [
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
        }
    }

    /**
     * Handle user type changed using only change data
     */
    private function handleUserTypeChangedFromData(MsgraphPendingChange $change): void
    {
        Log::info("Handling user type changed using change data", [
            'change_id' => $change->id,
            'user_id' => $change->user_id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'old_membership_type' => $change->old_membership_type,
            'new_membership_type' => $change->new_membership_type
        ]);

        // For type changes, we need both user and model to exist
        $user = User::find($change->user_id);
        $model = $this->findModelByTypeAndId($change->model_type, $change->model_id);

        if (!$user) {
            Log::warning("Cannot process user_type_changed - user no longer exists", [
                'change_id' => $change->id,
                'user_id' => $change->user_id
            ]);
            return;
        }

        if (!$model) {
            Log::warning("Cannot process user_type_changed - model no longer exists", [
                'change_id' => $change->id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
            return;
        }

        if (!$change->old_membership_type || !$change->new_membership_type) {
            Log::warning("Cannot process user_type_changed - missing membership type data", [
                'change_id' => $change->id,
                'old_type' => $change->old_membership_type,
                'new_type' => $change->new_membership_type
            ]);
            return;
        }

        $this->changeUserMembershipType($model, $user, $change->old_membership_type, $change->new_membership_type);
        Log::info("Successfully processed user_type_changed", [
            'user_id' => $change->user_id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'old_type' => $change->old_membership_type,
            'new_type' => $change->new_membership_type
        ]);
    }

    /**
     * Find model by type and ID
     */
    private function findModelByTypeAndId(string $modelType, int $modelId)
    {
        $modelClass = $this->getModelClassFromType($modelType);
        return $modelClass::find($modelId);
    }

    /**
     * Remove user from contact folders by model data (when model might not exist)
     */
    private function removeUserFromContactFoldersByModelData(int $userId, string $modelType, int $modelId, User $user): void
    {
        $modelClass = $this->getModelClassFromType($modelType);
        $contactFolders = ContactFolder::where('folderable_type', $modelClass)
            ->where('folderable_id', $modelId)
            ->whereNotNull('ms_folder_id')
            ->get();

        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            Log::warning('No Microsoft service users found for removing user from folders');
            return;
        }

        foreach ($serviceUsers as $serviceUser) {
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            foreach ($userFolders as $contactFolder) {
                try {
                    $this->removeContactFromFolder($contactFolder->ms_folder_id, $user, $serviceUser);
                    Log::info("Removed user from folder by model data", [
                        'user_id' => $userId,
                        'folder_name' => $contactFolder->display_name,
                        'service_user_email' => $serviceUser->email,
                        'model_type' => $modelType,
                        'model_id' => $modelId
                    ]);
                } catch (\Exception $e) {
                    Log::error("Failed to remove user from folder by model data", [
                        'user_id' => $userId,
                        'folder_id' => $contactFolder->id,
                        'service_user_email' => $serviceUser->email,
                        'error' => $e->getMessage(),
                        'model_type' => $modelType,
                        'model_id' => $modelId
                    ]);
                }
            }
        }
    }

    /**
     * Remove user from contact folders by user data (when user might not exist)
     */
    private function removeUserFromContactFoldersByUserData(int $userId, string $modelType, int $modelId): void
    {
        Log::info("Attempting to remove user by user data", [
            'user_id' => $userId,
            'model_type' => $modelType,
            'model_id' => $modelId
        ]);

        // Try to get user email from metadata if available
        $userEmail = null;
        // Note: In future implementations, consider passing user email from change metadata

        // Use the same logic as handleUserDeletedFromData for consistency
        $this->removeDeletedUserFromContactFoldersByModelData($userId, $userEmail, $modelType, $modelId);

        Log::info("Processed user removal by user data", [
            'user_id' => $userId,
            'model_type' => $modelType,
            'model_id' => $modelId,
            'note' => 'For better cleanup, consider storing user email in change metadata'
        ]);
    }

    /**
     * Get model class from model type string
     */
    private function getModelClassFromType(string $modelType): string
    {
        return $modelType === Committee::class ? Committee::class : WorkGroup::class;
    }

    /**
     * Handle user deleted using only change data
     */
    private function handleUserDeletedFromData(MsgraphPendingChange $change): void
    {
        Log::info("Handling user deleted using change data", [
            'change_id' => $change->id,
            'user_id' => $change->user_id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'old_membership_type' => $change->old_membership_type
        ]);

        // For user_deleted, we need to remove the user from all contact folders
        // The user is already deleted, so we'll work with stored data

        // Check if we have user email in metadata
        $userEmail = null;
        if (isset($change->metadata['user_email'])) {
            $userEmail = $change->metadata['user_email'];
            Log::info("Found user email in metadata", [
                'user_email' => $userEmail,
                'change_id' => $change->id
            ]);
        }

        // Find the model to get its contact folders
        $model = $this->findModelByTypeAndId($change->model_type, $change->model_id);

        if ($model) {
            // Model exists, remove user from its folders
            $this->removeDeletedUserFromContactFolders($change->user_id, $userEmail, $model);
            Log::info("Successfully processed user_deleted with existing model", [
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
        } else {
            // Model also deleted, remove user from folders by model data
            $this->removeDeletedUserFromContactFoldersByModelData($change->user_id, $userEmail, $change->model_type, $change->model_id);
            Log::info("Successfully processed user_deleted with deleted model", [
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
        }
    }

    /**
     * Remove deleted user from contact folders (when user is deleted but model exists)
     */
    private function removeDeletedUserFromContactFolders(int $userId, ?string $userEmail, $model): void
    {
        $contactFolders = $model->contactFolders()->whereNotNull('ms_folder_id')->get();
        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            Log::warning('No Microsoft service users found for removing deleted user from folders');
            return;
        }

        $removedContactsCount = 0;

        foreach ($serviceUsers as $serviceUser) {
            foreach ($contactFolders as $contactFolder) {
                try {
                    // If we have the user's email, we can find and remove their contacts directly
                    if ($userEmail) {
                        $userContacts = Contact::where('contact_folder_id', $contactFolder->id)
                            ->where('email', $userEmail)
                            ->get();

                        foreach ($userContacts as $contact) {
                            // Delete from Microsoft Graph
                            $this->apiClient->deleteWithRetry("users/{$serviceUser->email}/contacts/{$contact->ms_contact_id}");

                            // Delete from local database
                            $contact->delete();
                            $removedContactsCount++;

                            Log::info("Removed contact for deleted user", [
                                'user_id' => $userId,
                                'user_email' => $userEmail,
                                'contact_id' => $contact->id,
                                'folder_name' => $contactFolder->display_name,
                                'service_user_email' => $serviceUser->email
                            ]);
                        }
                    } else {
                        // No email available - log limitation
                        Log::warning("Cannot remove contacts for deleted user - no email available", [
                            'user_id' => $userId,
                            'folder_id' => $contactFolder->id,
                            'folder_name' => $contactFolder->display_name,
                            'service_user_email' => $serviceUser->email,
                            'suggestion' => 'Consider storing user email in change metadata for complete cleanup'
                        ]);
                    }
                } catch (\Exception $e) {
                    Log::error("Failed to remove deleted user from folder", [
                        'user_id' => $userId,
                        'user_email' => $userEmail,
                        'folder_id' => $contactFolder->id,
                        'service_user_email' => $serviceUser->email,
                        'error' => $e->getMessage()
                    ]);
                }
            }
        }

        Log::info("Completed removal of deleted user from contact folders", [
            'user_id' => $userId,
            'user_email' => $userEmail,
            'model_type' => get_class($model),
            'model_id' => $model->id,
            'removed_contacts_count' => $removedContactsCount
        ]);
    }

    /**
     * Remove deleted user from contact folders by model data (when both user and model might be deleted)
     */
    private function removeDeletedUserFromContactFoldersByModelData(int $userId, ?string $userEmail, string $modelType, int $modelId): void
    {
        $modelClass = $this->getModelClassFromType($modelType);
        $contactFolders = ContactFolder::where('folderable_type', $modelClass)
            ->where('folderable_id', $modelId)
            ->whereNotNull('ms_folder_id')
            ->get();

        $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                            ->where('is_new', false)
                            ->get();

        if ($serviceUsers->isEmpty()) {
            Log::warning('No Microsoft service users found for removing deleted user by model data');
            return;
        }

        $removedContactsCount = 0;

        foreach ($serviceUsers as $serviceUser) {
            $userFolders = $contactFolders->where('microsoft_user_id', $serviceUser->id);

            foreach ($userFolders as $contactFolder) {
                try {
                    // If we have the user's email, we can find and remove their contacts directly
                    if ($userEmail) {
                        $userContacts = Contact::where('contact_folder_id', $contactFolder->id)
                            ->where('email', $userEmail)
                            ->get();

                        foreach ($userContacts as $contact) {
                            // Delete from Microsoft Graph
                            $this->apiClient->deleteWithRetry("users/{$serviceUser->email}/contacts/{$contact->ms_contact_id}");

                            // Delete from local database
                            $contact->delete();
                            $removedContactsCount++;

                            Log::info("Removed contact for deleted user by model data", [
                                'user_id' => $userId,
                                'user_email' => $userEmail,
                                'contact_id' => $contact->id,
                                'folder_name' => $contactFolder->display_name,
                                'service_user_email' => $serviceUser->email,
                                'model_type' => $modelType,
                                'model_id' => $modelId
                            ]);
                        }
                    } else {
                        // No email available - log limitation
                        Log::warning("Cannot remove contacts for deleted user by model data - no email available", [
                            'user_id' => $userId,
                            'folder_id' => $contactFolder->id,
                            'folder_name' => $contactFolder->display_name,
                            'service_user_email' => $serviceUser->email,
                            'model_type' => $modelType,
                            'model_id' => $modelId,
                            'suggestion' => 'Consider storing user email in change metadata for complete cleanup'
                        ]);
                    }
                } catch (\Exception $e) {
                    Log::error("Failed to remove deleted user by model data", [
                        'user_id' => $userId,
                        'user_email' => $userEmail,
                        'folder_id' => $contactFolder->id,
                        'service_user_email' => $serviceUser->email,
                        'error' => $e->getMessage(),
                        'model_type' => $modelType,
                        'model_id' => $modelId
                    ]);
                }
            }
        }

        Log::info("Completed removal of deleted user by model data", [
            'user_id' => $userId,
            'user_email' => $userEmail,
            'model_type' => $modelType,
            'model_id' => $modelId,
            'removed_contacts_count' => $removedContactsCount
        ]);
    }

    /**
     * Handle user state changed using only change data
     */
    private function handleUserStateChangedFromData(MsgraphPendingChange $change): void
    {
        Log::info("Handling user state changed using change data", [
            'change_id' => $change->id,
            'user_id' => $change->user_id,
            'model_type' => $change->model_type,
            'model_id' => $change->model_id,
            'old_state' => $change->metadata['old_state'] ?? null,
            'new_state' => $change->metadata['new_state'] ?? null
        ]);

        // Extract states from metadata
        $oldState = $change->metadata['old_state'] ?? null;
        $newState = $change->metadata['new_state'] ?? null;

        if (!$oldState || !$newState) {
            Log::warning("Cannot process user_state_changed - missing state data", [
                'change_id' => $change->id,
                'user_id' => $change->user_id,
                'old_state' => $oldState,
                'new_state' => $newState
            ]);
            return;
        }

        // Try to find the user and model
        $user = User::find($change->user_id);
        $model = $this->findModelByTypeAndId($change->model_type, $change->model_id);

        if (!$user) {
            Log::warning("Cannot process user_state_changed - user no longer exists", [
                'change_id' => $change->id,
                'user_id' => $change->user_id
            ]);
            return;
        }

        if (!$model) {
            Log::warning("Cannot process user_state_changed - model no longer exists", [
                'change_id' => $change->id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
            return;
        }

        // If changed from inactive to active, add user to contact folders
        if ($oldState === 'inactive' && $newState === 'active') {
            $membershipType = $change->old_membership_type ?: $this->getCurrentMembershipType($model, $user);
            if ($membershipType) {
                $this->addUserToContactFolders($model, $user, $membershipType);
                Log::info("Successfully processed user_state_changed: activated user", [
                    'user_id' => $change->user_id,
                    'model_type' => $change->model_type,
                    'model_id' => $change->model_id,
                    'membership_type' => $membershipType
                ]);
            }
        }
        // If changed from active to inactive, remove user from contact folders
        elseif ($oldState === 'active' && $newState === 'inactive') {
            $this->removeUserFromContactFolders($model, $user);
            Log::info("Successfully processed user_state_changed: deactivated user", [
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id
            ]);
        }
        // Other state changes (e.g., pendingEmailValidation, pendingApproval) might not affect contact folders
        else {
            Log::info("User state change does not affect contact folders", [
                'user_id' => $change->user_id,
                'model_type' => $change->model_type,
                'model_id' => $change->model_id,
                'old_state' => $oldState,
                'new_state' => $newState
            ]);
        }
    }
}
