<?php

namespace App\Console\Commands;

use App\Models\ContactFolder;
use App\Models\Committee;
use App\Models\WorkGroup;
use App\Models\User;
use App\Models\MicrosoftUser;
use App\Services\MicrosoftGraph\SmartSyncService;
use App\Services\MicrosoftGraph\MicrosoftGraphApiClient;
use App\Services\MicrosoftGraph\MicrosoftGraphUtilities;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;

class SyncSmartChanges extends Command
{
    protected $signature = 'msgraph:smart-sync
                            {--dry-run : Show what would be synced without making changes}
                            {--compare-contacts : Compare contacts between Outlook and database without making changes}
                            {--sync-contacts : Synchronize Outlook contacts with database (applies only necessary changes)}
                            {--compare-folders : Compare contact folders between Outlook and database without making changes}
                            {--sync-folders : Synchronize contact folders with database (creates missing folders, updates names)}
                            {--sync-all : Synchronize folders first, then contacts for all Microsoft Users (complete sync)}
                            {--microsoft-user-id= : Target specific Microsoft User ID (optional, for contact/folder operations)}
                            {--contact-folder-id= : Target specific Contact Folder ID (requires microsoft-user-id)}
                            {--email-report= : Send email report to specified addresses (comma-separated: email1@test.com,email2@test.com)}
                            {--email-on-errors= : Send email report only when errors occur (optional comma-separated emails, uses default if empty)}';

    protected $description = 'Microsoft Graph Smart Sync - Contact sync options: --compare-contacts or --sync-contacts. Folder sync options: --compare-folders or --sync-folders. Complete sync: --sync-all (folders then contacts). Use [--microsoft-user-id=ID] [--contact-folder-id=ID] for targeting. Email reports: --email-report=email1@test.com,email2@test.com or --email-on-errors=email1@test.com,email2@test.com.';

    protected $smartSyncService;

    /**
     * Array to store defective contacts found during comparison
     * @var array
     */
    protected $defectiveContactsFound = [];

    public function __construct(SmartSyncService $smartSyncService)
    {
        parent::__construct();
        $this->smartSyncService = $smartSyncService;
    }

    public function handle()
    {
        $dryRun = $this->option('dry-run');
        $startTime = microtime(true);

        Log::info('Microsoft Graph Smart Sync Started');

        // Handle contact operations first
        if ($this->option('compare-contacts')) {
            return $this->handleContactComparison($dryRun);
        }

        if ($this->option('sync-contacts')) {
            return $this->handleContactSync($dryRun);
        }

        // Handle folder operations
        if ($this->option('compare-folders')) {
            return $this->handleFolderComparison($dryRun);
        }

        if ($this->option('sync-folders')) {
            return $this->handleFolderSync($dryRun);
        }

        // Handle complete sync (folders + contacts)
        if ($this->option('sync-all')) {
            return $this->handleCompletSync($dryRun);
        }

        // If no specific options provided, show help message
        $this->error('❌ No sync option specified. You must choose one of the following options:');
        $this->newLine();
        $this->info('📋 Available sync options:');
        $this->line('  • --compare-contacts    Compare contacts between Outlook and database');
        $this->line('  • --sync-contacts       Synchronize contacts with Outlook');
        $this->line('  • --compare-folders     Compare contact folders between Outlook and database');
        $this->line('  • --sync-folders        Synchronize contact folders with Outlook');
        $this->line('  • --sync-all            Complete sync: folders first, then contacts (recommended for cron)');
        $this->newLine();
        $this->info('� Email report options (use with --sync-all):');
        $this->line('  • --email-report=email1@test.com,email2@test.com    Send detailed report to specified emails (comma-separated)');
        $this->line('  • --email-on-errors[=emails]           Send report only when errors occur (optional comma-separated emails)');
        $this->newLine();
        $this->info('�💡 Examples:');
        $this->line('  • lando artisan msgraph:smart-sync --sync-all --dry-run');
        $this->line('  • lando artisan msgraph:smart-sync --sync-all --email-report=christian.freitas@eae.pt');
        $this->line('  • lando artisan msgraph:smart-sync --sync-all --email-report=admin1@eae.pt,admin2@eae.pt');
        $this->line('  • lando artisan msgraph:smart-sync --sync-all --email-on-errors');
        $this->line('  • lando artisan msgraph:smart-sync --sync-all --email-on-errors=admin1@eae.pt,admin2@eae.pt');
        $this->line('  • lando artisan msgraph:smart-sync --sync-contacts --microsoft-user-id=1');
        $this->line('  • lando artisan msgraph:smart-sync --compare-folders');
        $this->newLine();
        $this->info('ℹ️  Use --dry-run with any option to see what would be changed without making actual changes.');

        return 1; // Return error code to indicate missing required option
    }

    /**
     * Handle complete synchronization operation (folders first, then contacts)
     * Usage: php artisan msgraph:smart-sync --sync-all [--dry-run] [--email-report=email1@test.com,email2@test.com] [--email-on-errors=email1@test.com]
     */
    protected function handleCompletSync(bool $dryRun): int
    {
        $this->info('🚀 Starting Complete Microsoft Graph Synchronization...');
        if ($dryRun) {
            $this->warn('🧪 DRY RUN MODE - No changes will be made');
        }
        $this->newLine();

        $startTime = microtime(true);
        $totalErrors = 0;

        // Initialize detailed statistics
        $syncStats = [
            'start_time' => date('Y-m-d H:i:s'),
            'dry_run' => $dryRun,
            'folder_sync' => [
                'status' => 'pending',
                'users_processed' => 0,
                'models_processed' => 0,
                'folders_created' => 0,
                'folders_updated' => 0,
                'folders_deleted' => 0,
                'errors' => []
            ],
            'contact_sync' => [
                'status' => 'pending',
                'folders_processed' => 0,
                'contacts_added' => 0,
                'contacts_removed' => 0,
                'contacts_repaired' => 0,
                'errors' => []
            ]
        ];

        try {
            // Step 1: Synchronize folders first (required for contacts to work)
            $this->info('📁 STEP 1: Synchronizing Contact Folders');
            $this->info('='.str_repeat('=', 50));
            $this->newLine();

            $folderResult = $this->handleFolderSyncWithStats($dryRun, $syncStats);
            if ($folderResult !== 0) {
                $totalErrors++;
                $syncStats['folder_sync']['status'] = 'errors';
                $this->warn('⚠️  Folder synchronization completed with errors, but continuing with contact sync...');
            } else {
                $syncStats['folder_sync']['status'] = 'success';
                $this->info('✅ Folder synchronization completed successfully');
            }

            $this->newLine();
            $this->info('📧 STEP 2: Synchronizing Contacts');
            $this->info('='.str_repeat('=', 50));
            $this->newLine();

            // Step 2: Synchronize contacts (requires folders to exist)
            $contactResult = $this->handleContactSyncWithStats($dryRun, $syncStats);
            if ($contactResult !== 0) {
                $totalErrors++;
                $syncStats['contact_sync']['status'] = 'errors';
                $this->warn('⚠️  Contact synchronization completed with errors');
            } else {
                $syncStats['contact_sync']['status'] = 'success';
                $this->info('✅ Contact synchronization completed successfully');
            }

            // Calculate final statistics
            $executionTime = round(microtime(true) - $startTime, 2);
            $syncStats['end_time'] = date('Y-m-d H:i:s');
            $syncStats['execution_time'] = $executionTime;
            $syncStats['overall_status'] = $totalErrors === 0 ? 'success' : 'errors';
            $syncStats['total_errors'] = $totalErrors;

            // Display final summary
            $this->newLine();
            $this->info('🎯 COMPLETE SYNC SUMMARY');
            $this->info('='.str_repeat('=', 50));
            $this->table(
                ['Phase', 'Status'],
                [
                    ['Folder Sync', $folderResult === 0 ? '✅ Success' : '❌ Errors'],
                    ['Contact Sync', $contactResult === 0 ? '✅ Success' : '❌ Errors'],
                    ['Overall', $totalErrors === 0 ? '✅ Success' : "⚠️  {$totalErrors} phase(s) with errors"],
                    ['Execution Time', "{$executionTime} seconds"]
                ]
            );

            if ($totalErrors === 0) {
                $this->info('🎉 Complete synchronization finished successfully!');
                if ($dryRun) {
                    $this->info('💡 Run without --dry-run to apply the changes');
                }
            } else {
                $this->warn("⚠️  Complete synchronization finished with {$totalErrors} phase(s) having errors. Check logs for details.");
            }

            // Send email report if requested
            $this->sendEmailReportIfRequested($syncStats);

            return $totalErrors > 0 ? 1 : 0;

        } catch (\Exception $e) {
            $this->error('❌ Error during complete sync: ' . $e->getMessage());

            // Update sync stats with error information
            $syncStats['end_time'] = date('Y-m-d H:i:s');
            $syncStats['execution_time'] = round(microtime(true) - $startTime, 2);
            $syncStats['overall_status'] = 'critical_error';
            $syncStats['critical_error'] = $e->getMessage();
            $syncStats['total_errors'] = 1;

            // Send email report for critical errors
            $this->sendEmailReportIfRequested($syncStats);

            Log::error('Complete sync failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return 1;
        }
    }

    /**
     * Handle contact comparison operation
     * Usage: php artisan msgraph:smart-sync --compare-contacts [--microsoft-user-id=ID] [--contact-folder-id=ID]
     */
    protected function handleContactComparison(bool $dryRun): int
    {
        $this->info('🔍 Starting Contact Comparison...');
        $this->newLine();

        try {
            $targetFolders = $this->getTargetContactFolders();

            if ($targetFolders->isEmpty()) {
                $this->error('❌ No contact folders found matching the criteria');
                return 1;
            }

            $this->info("📁 Found {$targetFolders->count()} contact folder(s) to analyze");
            $this->newLine();

            $totalDifferences = 0;
            $apiClient = app(MicrosoftGraphApiClient::class);
            $apiClient->initialize();

            foreach ($targetFolders as $folder) {
                $this->info("🔍 Analyzing folder: {$folder->display_name}");
                $this->info("   Microsoft User: {$folder->microsoftUser->name} ({$folder->microsoftUser->email})");

                $differences = $this->compareContactsInFolder($folder, $apiClient);

                $this->displayContactDifferences($differences, $folder);
                $totalDifferences += count($differences['to_add']) + count($differences['to_remove']) + ($differences['defective_contacts'] ?? 0);

                $this->newLine();
            }

            $this->info("📊 Summary: {$totalDifferences} total differences found across all folders");

            if ($totalDifferences > 0) {
                $this->info("💡 Use --sync-contacts to apply these changes and repair defective contacts");
            }

            return 0;

        } catch (\Exception $e) {
            $this->error('❌ Error during contact comparison: ' . $e->getMessage());
            Log::error('Contact comparison failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return 1;
        }
    }

    /**
     * Handle contact synchronization operation
     * Usage: php artisan msgraph:smart-sync --sync-contacts [--microsoft-user-id=ID] [--contact-folder-id=ID] [--dry-run]
     */
    protected function handleContactSync(bool $dryRun): int
    {
        $this->info('🔄 Starting Contact Synchronization...');
        if ($dryRun) {
            $this->warn('🧪 DRY RUN MODE - No changes will be made');
        }
        $this->newLine();

        try {
            $targetFolders = $this->getTargetContactFolders();

            if ($targetFolders->isEmpty()) {
                $this->error('❌ No contact folders found matching the criteria');
                return 1;
            }

            $this->info("📁 Found {$targetFolders->count()} contact folder(s) to synchronize");
            $this->newLine();

            $summary = [
                'folders_processed' => 0,
                'contacts_added' => 0,
                'contacts_removed' => 0,
                'contacts_repaired' => 0,
                'errors' => []
            ];

            $apiClient = app(MicrosoftGraphApiClient::class);
            $apiClient->initialize();

            foreach ($targetFolders as $folder) {
                $this->info("🔄 Synchronizing folder: {$folder->display_name}");
                $this->info("   Microsoft User: {$folder->microsoftUser->name} ({$folder->microsoftUser->email})");

                $differences = $this->compareContactsInFolder($folder, $apiClient);
                $folderResults = $this->applySyncChanges($folder, $differences, $apiClient, $dryRun);

                // Update summary
                $summary['folders_processed']++;
                $summary['contacts_added'] += $folderResults['added'];
                $summary['contacts_removed'] += $folderResults['removed'];
                $summary['contacts_repaired'] += $folderResults['repaired'] ?? 0;
                $summary['errors'] = array_merge($summary['errors'], $folderResults['errors']);

                $this->info("   ✅ Added: {$folderResults['added']}, Removed: {$folderResults['removed']}, Repaired: " . ($folderResults['repaired'] ?? 0));
                if (!empty($folderResults['errors'])) {
                    $this->warn("   ⚠️  Errors: " . count($folderResults['errors']));
                }

                $this->newLine();
            }

            // Display final summary
            $this->info('📊 Synchronization Summary:');
            $this->table(
                ['Metric', 'Count'],
                [
                    ['Folders Processed', $summary['folders_processed']],
                    ['Contacts Added', $summary['contacts_added']],
                    ['Contacts Removed', $summary['contacts_removed']],
                    ['Contacts Repaired', $summary['contacts_repaired']],
                    ['Errors', count($summary['errors'])]
                ]
            );

            if (!empty($summary['errors'])) {
                $this->newLine();
                $this->error('❌ Errors encountered:');
                foreach ($summary['errors'] as $error) {
                    $this->line("  • {$error}");
                }
            }

            return count($summary['errors']) > 0 ? 1 : 0;

        } catch (\Exception $e) {
            $this->error('❌ Error during contact synchronization: ' . $e->getMessage());
            Log::error('Contact synchronization failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return 1;
        }
    }

    /**
     * Get target contact folders based on command options
     */
    protected function getTargetContactFolders()
    {
        $microsoftUserId = $this->option('microsoft-user-id');
        $contactFolderId = $this->option('contact-folder-id');

        // Validate parameters
        if ($contactFolderId && !$microsoftUserId) {
            throw new \InvalidArgumentException('--contact-folder-id requires --microsoft-user-id');
        }

        $query = ContactFolder::with('microsoftUser')->whereNotNull('ms_folder_id');

        if ($microsoftUserId) {
            $query->where('microsoft_user_id', $microsoftUserId);

            if ($contactFolderId) {
                $query->where('id', $contactFolderId);
            }
        }

        return $query->get(); // Devuelve Collection, no array
    }

    /**
     * Compare contacts in a specific folder between Outlook and database
     */
    protected function compareContactsInFolder(ContactFolder $folder, MicrosoftGraphApiClient $apiClient): array
    {
        // Get contacts that SHOULD be in this folder based on committee/workgroup user membership
        $expectedContacts = $this->getExpectedContactsForFolder($folder);

        // Get contacts that ARE currently in Outlook
        $outlookContacts = $this->getOutlookContacts($folder, $apiClient);

        // Compare and find differences
        $differences = [
            'to_add' => array_diff($expectedContacts, $outlookContacts), // Expected but not in Outlook
            'to_remove' => array_diff($outlookContacts, $expectedContacts), // In Outlook but not expected
            'in_sync' => array_intersect($expectedContacts, $outlookContacts), // In both (already synced)
            'defective_contacts' => isset($this->defectiveContactsFound[$folder->id]) ? count($this->defectiveContactsFound[$folder->id]) : 0
        ];

        Log::info('Contact comparison completed', [
            'folder_id' => $folder->id,
            'folder_name' => $folder->display_name,
            'folderable_type' => $folder->folderable_type,
            'folderable_id' => $folder->folderable_id,
            'expected_contacts' => count($expectedContacts),
            'outlook_contacts' => count($outlookContacts),
            'to_add' => count($differences['to_add']),
            'to_remove' => count($differences['to_remove']),
            'in_sync' => count($differences['in_sync']),
            'defective_contacts' => $differences['defective_contacts']
        ]);

        return $differences;
    }

    /**
     * Get expected contacts for a folder based on committee/workgroup user membership
     */
    protected function getExpectedContactsForFolder(ContactFolder $folder): array
    {
        $contacts = [];

        // Determine if this is a TO or CC folder based on display_name
        $folderType = null;
        if (str_ends_with($folder->display_name, ' TO')) {
            $folderType = 'TO';
        } elseif (str_ends_with($folder->display_name, ' CC')) {
            $folderType = 'CC';
        } else {
            Log::warning('Could not determine folder type from display_name', [
                'folder_id' => $folder->id,
                'display_name' => $folder->display_name
            ]);
            return [];
        }

        Log::info('Getting expected contacts for folder', [
            'folder_id' => $folder->id,
            'folder_name' => $folder->display_name,
            'folder_type' => $folderType,
            'folderable_type' => $folder->folderable_type,
            'folderable_id' => $folder->folderable_id
        ]);

        // Get users based on folderable_type
        if ($folder->folderable_type === 'App\\Models\\Committee') {
            $committee = Committee::find($folder->folderable_id);
            if ($committee) {
                // Get users with the matching pivot type (TO or CC)
                $users = $committee->users()->wherePivot('type', $folderType)->get();
                foreach ($users as $user) {
                    if (!empty($user->email)) {
                        $contacts[] = $user->email;
                    }
                }

                Log::info('Found committee users', [
                    'committee_id' => $committee->id,
                    'committee_name' => $committee->name,
                    'type' => $folderType,
                    'users_count' => count($contacts)
                ]);
            }
        } elseif ($folder->folderable_type === 'App\\Models\\WorkGroup') {
            $workGroup = WorkGroup::find($folder->folderable_id);
            if ($workGroup) {
                // Get users with the matching pivot type (TO or CC)
                $users = $workGroup->users()->wherePivot('type', $folderType)->get();
                foreach ($users as $user) {
                    if (!empty($user->email)) {
                        $contacts[] = $user->email;
                    }
                }

                Log::info('Found workgroup users', [
                    'workgroup_id' => $workGroup->id,
                    'workgroup_name' => $workGroup->name,
                    'type' => $folderType,
                    'users_count' => count($contacts)
                ]);
            }
        } else {
            Log::warning('Unknown folderable_type', [
                'folder_id' => $folder->id,
                'folderable_type' => $folder->folderable_type
            ]);
        }

        return array_unique($contacts);
    }

    /**
     * Get contacts from Outlook for a specific folder (with pagination support)
     */
    protected function getOutlookContacts(ContactFolder $folder, MicrosoftGraphApiClient $apiClient): array
    {
        $endpoint = "users/{$folder->microsoftUser->email}/contactFolders/{$folder->ms_folder_id}/contacts?\$select=id,emailAddresses,displayName,givenName,surname";

        // Use the existing pagination method from MicrosoftGraphApiClient
        $allContacts = $apiClient->getAllPaginatedItems($endpoint);

        // Extract email addresses from contacts and identify defective ones
        $contacts = [];
        $defectiveContacts = [];

        foreach ($allContacts as $contact) {
            $contactObj = is_object($contact) ? $contact : (object) $contact;
            if (isset($contactObj->emailAddresses) && !empty($contactObj->emailAddresses)) {
                $emailObj = is_object($contactObj->emailAddresses[0])
                    ? $contactObj->emailAddresses[0]
                    : (object) $contactObj->emailAddresses[0];

                if (isset($emailObj->address)) {
                    $contacts[] = $emailObj->address;

                    // Check if contact is defective (missing givenName)
                    if (empty($contactObj->givenName)) {
                        $defectiveContacts[] = [
                            'id' => $contactObj->id ?? null,
                            'email' => $emailObj->address,
                            'displayName' => $contactObj->displayName ?? '',
                            'givenName' => $contactObj->givenName ?? '',
                            'surname' => $contactObj->surname ?? ''
                        ];
                    }
                }
            }
        }

        // Report defective contacts found
        if (!empty($defectiveContacts)) {
            Log::warning('Found defective contacts (missing givenName)', [
                'folder_id' => $folder->id,
                'folder_name' => $folder->display_name,
                'defective_count' => count($defectiveContacts),
                'defective_contacts' => array_column($defectiveContacts, 'email')
            ]);
        }

        Log::info('Retrieved Outlook contacts with pagination', [
            'folder_id' => $folder->id,
            'folder_name' => $folder->display_name,
            'total_contacts' => count($contacts),
            'defective_contacts' => count($defectiveContacts)
        ]);

        // Store defective contacts for potential repair
        $this->storeDefectiveContacts($folder, $defectiveContacts);

        return array_unique($contacts);
    }

    /**
     * Store defective contacts for later repair
     */
    protected function storeDefectiveContacts(ContactFolder $folder, array $defectiveContacts): void
    {
        $this->defectiveContactsFound[$folder->id] = $defectiveContacts;
    }

    /**
     * Get and repair defective contacts in a folder
     */
    protected function repairDefectiveContactsInFolder(ContactFolder $folder, MicrosoftGraphApiClient $apiClient, bool $dryRun = false): array
    {
        $results = [
            'repaired' => 0,
            'errors' => []
        ];

        if (!isset($this->defectiveContactsFound[$folder->id])) {
            return $results;
        }

        $defectiveContacts = $this->defectiveContactsFound[$folder->id];

        foreach ($defectiveContacts as $defectiveContact) {
            try {
                if (!$defectiveContact['id']) {
                    continue; // Skip if no contact ID
                }

                $email = $defectiveContact['email'];

                // Find the user in our database to get proper names
                $user = User::where('email', $email)->first();

                $givenName = '';
                $surname = '';

                if ($user) {
                    $givenName = $user->name ?? '';
                    $surname = $user->last_name ?? '';
                }

                // If still no givenName, extract from email
                if (empty($givenName)) {
                    $givenName = $this->extractNameFromEmail($email);
                }

                // Prepare update data
                $updateData = [
                    'displayName' => $user ? $user->name : $givenName,
                    'givenName' => $givenName
                ];

                if (!empty($surname)) {
                    $updateData['surname'] = $surname;
                }

                if (!$dryRun) {
                    $apiClient->patchWithRetry(
                        "users/{$folder->microsoftUser->email}/contactFolders/{$folder->ms_folder_id}/contacts/{$defectiveContact['id']}",
                        $updateData,
                        ['operation' => 'repair_defective_contact']
                    );
                }

                $results['repaired']++;

                if ($dryRun) {
                    $this->line("     🧪 Would repair defective contact: {$email} (givenName: '{$givenName}')");
                } else {
                    $this->line("     ✅ Repaired defective contact: {$email} (givenName: '{$givenName}')");
                }

                Log::info('Defective contact repaired', [
                    'email' => $email,
                    'contact_id' => $defectiveContact['id'],
                    'folder_id' => $folder->id,
                    'given_name' => $givenName,
                    'surname' => $surname,
                    'dry_run' => $dryRun
                ]);

            } catch (\Exception $e) {
                $error = "Failed to repair defective contact {$defectiveContact['email']}: " . $e->getMessage();
                $results['errors'][] = $error;
                $this->line("     ❌ Error repairing {$defectiveContact['email']}: " . $e->getMessage());
            }
        }

        return $results;
    }

    /**
     * Extract a clean name from email address
     */
    protected function extractNameFromEmail(string $email): string
    {
        $localPart = explode('@', $email)[0];

        // Remove common symbols and replace with spaces
        $cleanName = str_replace(['.', '_', '-', '+', '='], ' ', $localPart);

        // Remove numbers and special characters, keep only letters and spaces
        $cleanName = preg_replace('/[^a-zA-Z\s]/', '', $cleanName);

        // Clean up spaces and capitalize
        $cleanName = trim(preg_replace('/\s+/', ' ', $cleanName));
        $cleanName = ucwords(strtolower($cleanName));

        // If still empty, use the original local part
        if (empty($cleanName)) {
            $cleanName = ucfirst($localPart);
        }

        return $cleanName;
    }

    /**
     * Display contact differences in a formatted table
     */
    protected function displayContactDifferences(array $differences, ContactFolder $folder): void
    {
        $toAddCount = count($differences['to_add']);
        $toRemoveCount = count($differences['to_remove']);
        $inSyncCount = count($differences['in_sync']);
        $defectiveCount = $differences['defective_contacts'] ?? 0;

        $this->table(
            ['Status', 'Count', 'Description'],
            [
                ['✅ In Sync', $inSyncCount, 'Contacts that match in both systems'],
                ['➕ To Add', $toAddCount, 'Expected users missing in Outlook'],
                ['➖ To Remove', $toRemoveCount, 'Contacts in Outlook but not expected'],
                ['🔧 Defective', $defectiveCount, 'Contacts missing givenName (need repair)']
            ]
        );

        if ($toAddCount > 0) {
            $this->warn("   Contacts to add to Outlook:");
            foreach (array_slice($differences['to_add'], 0, 5) as $email) {
                $this->line("     • {$email}");
            }
            if ($toAddCount > 5) {
                $this->line("     ... and " . ($toAddCount - 5) . " more");
            }
        }

        if ($toRemoveCount > 0) {
            $this->warn("   Contacts to remove from Outlook:");
            foreach (array_slice($differences['to_remove'], 0, 5) as $email) {
                $this->line("     • {$email}");
            }
            if ($toRemoveCount > 5) {
                $this->line("     ... and " . ($toRemoveCount - 5) . " more");
            }
        }

        if ($defectiveCount > 0) {
            $this->warn("   Defective contacts (missing givenName):");
            if (isset($this->defectiveContactsFound[$folder->id])) {
                $defectiveContacts = $this->defectiveContactsFound[$folder->id];
                foreach (array_slice($defectiveContacts, 0, 5) as $contact) {
                    $this->line("     • {$contact['email']} (displayName: '{$contact['displayName']}')");
                }
                if ($defectiveCount > 5) {
                    $this->line("     ... and " . ($defectiveCount - 5) . " more");
                }
            }
        }
    }

    /**
     * Apply synchronization changes to Outlook
     */
    protected function applySyncChanges(ContactFolder $folder, array $differences, MicrosoftGraphApiClient $apiClient, bool $dryRun): array
    {
        $results = [
            'added' => 0,
            'removed' => 0,
            'repaired' => 0,
            'errors' => []
        ];

        // Repair defective contacts first
        $defectiveCount = $differences['defective_contacts'] ?? 0;
        if ($defectiveCount > 0) {
            $this->info("     🔧 Repairing defective contacts...");
            $repairResults = $this->repairDefectiveContactsInFolder($folder, $apiClient, $dryRun);
            $results['repaired'] = $repairResults['repaired'];
            $results['errors'] = array_merge($results['errors'], $repairResults['errors']);
        }

        // Add missing contacts to Outlook
        foreach ($differences['to_add'] as $email) {
            try {
                // Add the contact to Outlook
                if (!$dryRun) {
                    $this->addContactToOutlookFromUser($email, $folder, $apiClient);
                }
                $results['added']++;

                if ($dryRun) {
                    $this->line("     🧪 Would add: {$email}");
                } else {
                    $this->line("     ✅ Added: {$email}");
                }

            } catch (\Exception $e) {
                $error = "Failed to add contact {$email}: " . $e->getMessage();
                $results['errors'][] = $error;
                $this->line("     ❌ Error adding {$email}: " . $e->getMessage());
            }
        }

        // Remove extra contacts from Outlook
        foreach ($differences['to_remove'] as $email) {
            try {
                if (!$dryRun) {
                    $this->removeContactFromOutlook($email, $folder, $apiClient);
                }
                $results['removed']++;

                if ($dryRun) {
                    $this->line("     🧪 Would remove: {$email}");
                } else {
                    $this->line("     ✅ Removed: {$email}");
                }

            } catch (\Exception $e) {
                $error = "Failed to remove contact {$email}: " . $e->getMessage();
                $results['errors'][] = $error;
                $this->line("     ❌ Error removing {$email}: " . $e->getMessage());
            }
        }

        return $results;
    }

    /**
     * Add a contact to Outlook based on user email
     */
    protected function addContactToOutlookFromUser(string $email, ContactFolder $folder, MicrosoftGraphApiClient $apiClient): void
    {
        // Find the user by email
        $user = User::where('email', $email)->first();

        if (!$user) {
            // Create a minimal user object for the utility function
            $user = (object)[
                'email' => $email,
                'name' => $email, // Use email as fallback name
                'first_name' => '',
                'last_name' => ''
            ];
        }

        // Determine folder type (TO or CC) from folder display name
        $folderType = MicrosoftGraphUtilities::extractFolderType($folder->display_name);

        // Use the utility function to create contact data
        $contactData = MicrosoftGraphUtilities::createContactData($user, $folderType);

        $response = $apiClient->postWithRetry(
            "users/{$folder->microsoftUser->email}/contactFolders/{$folder->ms_folder_id}/contacts",
            $contactData,
            ['operation' => 'add_contact_to_outlook']
        );

        // Log the operation (no need to store in database)
        Log::info('Contact added to Outlook', [
            'email' => $email,
            'display_name' => $contactData['displayName'] ?? $email,
            'folder_id' => $folder->id,
            'folder_name' => $folder->display_name,
            'folder_type' => $folderType
        ]);
    }

    /**
     * Remove a contact from Outlook
     */
    protected function removeContactFromOutlook(string $email, ContactFolder $folder, MicrosoftGraphApiClient $apiClient): void
    {
        // First, find the contact in Outlook to get its ID
        $contacts = $this->getOutlookContactsWithIds($folder, $apiClient);
        $contactId = null;

        foreach ($contacts as $contact) {
            if ($contact['email'] === $email) {
                $contactId = $contact['id'];
                break;
            }
        }

        if ($contactId) {
            $apiClient->deleteWithRetry(
                "users/{$folder->microsoftUser->email}/contactFolders/{$folder->ms_folder_id}/contacts/{$contactId}",
                ['operation' => 'remove_contact_from_outlook']
            );
        }
    }

    /**
     * Get contacts from Outlook with their IDs (for deletion operations)
     */
    protected function getOutlookContactsWithIds(ContactFolder $folder, MicrosoftGraphApiClient $apiClient): array
    {
        $endpoint = "users/{$folder->microsoftUser->email}/contactFolders/{$folder->ms_folder_id}/contacts?\$select=id,emailAddresses";

        // Use the existing pagination method from MicrosoftGraphApiClient
        $allContacts = $apiClient->getAllPaginatedItems($endpoint);

        // Extract contact IDs and email addresses
        $contacts = [];
        foreach ($allContacts as $contact) {
            $contactObj = is_object($contact) ? $contact : (object) $contact;
            if (isset($contactObj->emailAddresses) && !empty($contactObj->emailAddresses) && isset($contactObj->id)) {
                $emailObj = is_object($contactObj->emailAddresses[0])
                    ? $contactObj->emailAddresses[0]
                    : (object) $contactObj->emailAddresses[0];
                if (isset($emailObj->address)) {
                    $contacts[] = [
                        'id' => $contactObj->id,
                        'email' => $emailObj->address
                    ];
                }
            }
        }

        Log::info('Retrieved Outlook contacts with IDs using pagination', [
            'folder_id' => $folder->id,
            'folder_name' => $folder->display_name,
            'total_contacts' => count($contacts)
        ]);

        return $contacts;
    }

    /**
     * Handle folder comparison operation
     * Usage: php artisan msgraph:smart-sync --compare-folders [--microsoft-user-id=ID]
     */
    protected function handleFolderComparison(bool $dryRun): int
    {
        $this->info('🔍 Starting Contact Folder Comparison...');
        $this->newLine();

        try {
            $microsoftUserId = $this->option('microsoft-user-id');

            // Get target Microsoft Users
            if ($microsoftUserId) {
                $targetMicrosoftUsers = collect([MicrosoftUser::find($microsoftUserId)]);
                if ($targetMicrosoftUsers->first() === null) {
                    $this->error("❌ Microsoft User with ID {$microsoftUserId} not found");
                    return 1;
                }
            } else {
                $targetMicrosoftUsers = MicrosoftUser::where('is_new', 0)->get();
                if ($targetMicrosoftUsers->isEmpty()) {
                    $this->error('❌ No active Microsoft Users found (is_new = 0)');
                    return 1;
                }
            }

            $this->info("� Analyzing " . $targetMicrosoftUsers->count() . " Microsoft User(s)");
            $this->newLine();

            $totalDifferences = 0;
            $apiClient = app(MicrosoftGraphApiClient::class);
            $apiClient->initialize();

            // Process each Microsoft User
            foreach ($targetMicrosoftUsers as $microsoftUser) {
                $this->info("🔍 Analyzing Microsoft User: {$microsoftUser->name} ({$microsoftUser->email})");

                $userDifferences = $this->compareFoldersForMicrosoftUser($microsoftUser, $apiClient);
                $totalDifferences += $userDifferences;

                $this->newLine();
            }

            $this->info("📊 Summary: {$totalDifferences} total differences found across all users");

            if ($totalDifferences > 0) {
                $this->info("💡 Use --sync-folders to apply these changes");
            }

            return 0;

        } catch (\Exception $e) {
            $this->error('❌ Error during folder comparison: ' . $e->getMessage());
            Log::error('Folder comparison failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return 1;
        }
    }

    /**
     * Compare folders for a specific Microsoft User
     */
    protected function compareFoldersForMicrosoftUser(MicrosoftUser $microsoftUser, MicrosoftGraphApiClient $apiClient): int
    {
        $targetModels = $this->getTargetModelsForFolders();
        $userTotalDifferences = 0;

        foreach ($targetModels as $modelData) {
            $model = $modelData['model'];
            $modelType = $modelData['type'];
            $modelName = $model->title;

            $this->info("  🔍 Analyzing {$modelType}: {$modelName}");

            $differences = $this->compareFoldersForModel($model, $modelType, $apiClient, $microsoftUser->id);

            $this->displayFolderDifferences($differences, $model, $modelType);
            $userTotalDifferences += count($differences['to_create']) + count($differences['to_update']);
        }

        // Check for orphaned folders (folders whose committee/workgroup no longer exists)
        $orphanedFolders = $this->findOrphanedFolders($microsoftUser->id);
        if (!empty($orphanedFolders)) {
            $this->info("  🗑️  Found orphaned folders:");
            foreach ($orphanedFolders as $folder) {
                $this->warn("    • {$folder->display_name} (ID: {$folder->id}) - {$folder->folderable_type} ID {$folder->folderable_id} no longer exists");
            }
            $userTotalDifferences += count($orphanedFolders);
        }

        $this->info("  📊 User differences: {$userTotalDifferences}");
        return $userTotalDifferences;
    }

    /**
     * Handle folder synchronization operation
     * Usage: php artisan msgraph:smart-sync --sync-folders [--microsoft-user-id=ID] [--dry-run]
     */
    protected function handleFolderSync(bool $dryRun): int
    {
        $this->info('🔄 Starting Contact Folder Synchronization...');
        if ($dryRun) {
            $this->warn('🧪 DRY RUN MODE - No changes will be made');
        }
        $this->newLine();

        try {
            $microsoftUserId = $this->option('microsoft-user-id');

            // Get target Microsoft Users
            if ($microsoftUserId) {
                $targetMicrosoftUsers = collect([MicrosoftUser::find($microsoftUserId)]);
                if ($targetMicrosoftUsers->first() === null) {
                    $this->error("❌ Microsoft User with ID {$microsoftUserId} not found");
                    return 1;
                }
            } else {
                $targetMicrosoftUsers = MicrosoftUser::where('is_new', 0)->get();
                if ($targetMicrosoftUsers->isEmpty()) {
                    $this->error('❌ No active Microsoft Users found (is_new = 0)');
                    return 1;
                }
            }

            $this->info("� Processing " . $targetMicrosoftUsers->count() . " Microsoft User(s)");
            $this->newLine();

            $globalSummary = [
                'users_processed' => 0,
                'models_processed' => 0,
                'folders_created' => 0,
                'folders_updated' => 0,
                'folders_deleted' => 0,
                'errors' => []
            ];

            $apiClient = app(MicrosoftGraphApiClient::class);
            $apiClient->initialize();

            // Process each Microsoft User
            foreach ($targetMicrosoftUsers as $microsoftUser) {
                $this->info("🔄 Processing Microsoft User: {$microsoftUser->name} ({$microsoftUser->email})");

                $userResults = $this->syncFoldersForMicrosoftUser($microsoftUser, $apiClient, $dryRun);

                // Update global summary
                $globalSummary['users_processed']++;
                $globalSummary['models_processed'] += $userResults['models_processed'];
                $globalSummary['folders_created'] += $userResults['folders_created'];
                $globalSummary['folders_updated'] += $userResults['folders_updated'];
                $globalSummary['folders_deleted'] += $userResults['folders_deleted'];
                $globalSummary['errors'] = array_merge($globalSummary['errors'], $userResults['errors']);

                $this->info("   ✅ Models: {$userResults['models_processed']}, Created: {$userResults['folders_created']}, Updated: {$userResults['folders_updated']}, Deleted: {$userResults['folders_deleted']}");
                if (!empty($userResults['errors'])) {
                    $this->warn("   ⚠️  Errors: " . count($userResults['errors']));
                }
                $this->newLine();
            }

            // Display final summary
            $this->info('📊 Folder Synchronization Summary:');
            $this->table(
                ['Metric', 'Count'],
                [
                    ['Microsoft Users Processed', $globalSummary['users_processed']],
                    ['Models Processed', $globalSummary['models_processed']],
                    ['Folders Created', $globalSummary['folders_created']],
                    ['Folders Updated', $globalSummary['folders_updated']],
                    ['Folders Deleted (Orphaned)', $globalSummary['folders_deleted']],
                    ['Errors', count($globalSummary['errors'])]
                ]
            );

            if (!empty($globalSummary['errors'])) {
                $this->newLine();
                $this->error('❌ Errors encountered:');
                foreach ($globalSummary['errors'] as $error) {
                    $this->line("  • {$error}");
                }
            }

            return count($globalSummary['errors']) > 0 ? 1 : 0;

        } catch (\Exception $e) {
            $this->error('❌ Error during folder synchronization: ' . $e->getMessage());
            Log::error('Folder synchronization failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return 1;
        }
    }

    /**
     * Sync folders for a specific Microsoft User
     */
    protected function syncFoldersForMicrosoftUser(MicrosoftUser $microsoftUser, MicrosoftGraphApiClient $apiClient, bool $dryRun): array
    {
        $targetModels = $this->getTargetModelsForFolders();

        $summary = [
            'models_processed' => 0,
            'folders_created' => 0,
            'folders_updated' => 0,
            'folders_deleted' => 0,
            'errors' => []
        ];

        foreach ($targetModels as $modelData) {
            $model = $modelData['model'];
            $modelType = $modelData['type'];
            $modelName = $model->title;

            $differences = $this->compareFoldersForModel($model, $modelType, $apiClient, $microsoftUser->id);
            $modelResults = $this->applyFolderSyncChanges($model, $modelType, $differences, $apiClient, $dryRun, $microsoftUser->id);

            // Update summary
            $summary['models_processed']++;
            $summary['folders_created'] += $modelResults['created'];
            $summary['folders_updated'] += $modelResults['updated'];
            $summary['errors'] = array_merge($summary['errors'], $modelResults['errors']);
        }

        // Handle orphaned folders
        $orphanedFolders = $this->findOrphanedFolders($microsoftUser->id);
        if (!empty($orphanedFolders)) {
            $this->info("  🗑️  Processing orphaned folders...");
            $orphanResults = $this->deleteOrphanedFolders($orphanedFolders, $apiClient, $dryRun);

            $summary['folders_deleted'] += $orphanResults['deleted'];
            $summary['errors'] = array_merge($summary['errors'], $orphanResults['errors']);
        }

        return $summary;
    }

    /**
     * Get target committees and workgroups for folder operations
     */
    protected function getTargetModelsForFolders(): array
    {
        $models = [];

        // Get committees
        $committees = Committee::all();
        foreach ($committees as $committee) {
            $models[] = [
                'model' => $committee,
                'type' => 'Committee'
            ];
        }

        // Get workgroups
        $workGroups = WorkGroup::all();
        foreach ($workGroups as $workGroup) {
            $models[] = [
                'model' => $workGroup,
                'type' => 'WorkGroup'
            ];
        }

        Log::info('Found models for folder operations', [
            'committees' => count($committees),
            'workgroups' => count($workGroups),
            'total' => count($models)
        ]);

        return $models;
    }

    /**
     * Find orphaned folders (folders whose committee/workgroup no longer exists)
     */
    protected function findOrphanedFolders(int $microsoftUserId): array
    {
        $orphanedFolders = [];

        // Get all contact folders for this Microsoft User
        $contactFolders = ContactFolder::with('microsoftUser')
            ->where('microsoft_user_id', $microsoftUserId)
            ->get();

        foreach ($contactFolders as $folder) {
            $isOrphaned = false;

            if ($folder->folderable_type === 'App\\Models\\Committee') {
                // Check if the committee still exists
                $committee = Committee::find($folder->folderable_id);
                if (!$committee) {
                    $isOrphaned = true;
                }
            } elseif ($folder->folderable_type === 'App\\Models\\WorkGroup') {
                // Check if the workgroup still exists
                $workGroup = WorkGroup::find($folder->folderable_id);
                if (!$workGroup) {
                    $isOrphaned = true;
                }
            } else {
                // Unknown folderable_type, consider it orphaned
                $isOrphaned = true;
            }

            if ($isOrphaned) {
                $orphanedFolders[] = $folder;
            }
        }

        Log::info('Orphaned folders found', [
            'microsoft_user_id' => $microsoftUserId,
            'orphaned_count' => count($orphanedFolders)
        ]);

        return $orphanedFolders;
    }

    /**
     * Delete orphaned folders from Outlook and database
     */
    protected function deleteOrphanedFolders(array $orphanedFolders, MicrosoftGraphApiClient $apiClient, bool $dryRun): array
    {
        $results = [
            'deleted' => 0,
            'errors' => []
        ];

        foreach ($orphanedFolders as $folder) {
            try {
                if (!$dryRun) {
                    // Delete from Outlook first
                    if ($folder->ms_folder_id) {

                        $apiClient->postWithRetry(
                            "users/{$folder->microsoftUser->email}/contactFolders/{$folder->ms_folder_id}/permanentDelete",
                            [],
                            [
                                'operation' => 'delete_orphaned_folder',
                                'folder_id' => $folder->ms_folder_id,
                                'user_email' => $folder->microsoftUser->email
                            ]
                        );
                    }

                    // Then delete from our database
                    $folder->delete();
                }

                $results['deleted']++;

                if ($dryRun) {
                    $this->line("     🧪 Would delete: {$folder->display_name} (orphaned from {$folder->folderable_type})");
                } else {
                    $this->line("     ✅ Deleted: {$folder->display_name} (orphaned from {$folder->folderable_type})");
                }

                Log::info('Orphaned folder deleted', [
                    'folder_id' => $folder->id,
                    'folder_name' => $folder->display_name,
                    'ms_folder_id' => $folder->ms_folder_id,
                    'folderable_type' => $folder->folderable_type,
                    'folderable_id' => $folder->folderable_id,
                    'microsoft_user_id' => $folder->microsoft_user_id,
                    'dry_run' => $dryRun
                ]);

            } catch (\Exception $e) {
                $error = "Failed to delete orphaned folder {$folder->display_name}: " . $e->getMessage();
                $results['errors'][] = $error;
                $this->line("     ❌ Error deleting {$folder->display_name}: " . $e->getMessage());
            }
        }

        return $results;
    }

    /**
     * Compare folders for a specific model (Committee or WorkGroup)
     */
    protected function compareFoldersForModel($model, string $modelType, MicrosoftGraphApiClient $apiClient, ?int $microsoftUserId = null): array
    {
        $modelClass = 'App\\Models\\' . $modelType;
        $modelName = $model->title;

        // Get expected folder names (TO and CC) - sanitized for consistency
        $expectedFolders = [
            'TO' => MicrosoftGraphUtilities::sanitizeFolderDisplayName("{$modelName} TO"),
            'CC' => MicrosoftGraphUtilities::sanitizeFolderDisplayName("{$modelName} CC")
        ];

        // Get existing folders from our database for this specific Microsoft User
        $query = ContactFolder::where('folderable_type', $modelClass)
            ->where('folderable_id', $model->id);

        // Filter by Microsoft User if specified
        if ($microsoftUserId) {
            $query->where('microsoft_user_id', $microsoftUserId);
        }

        $existingFolders = $query->get()
            ->keyBy(function($folder) {
                return str_ends_with($folder->display_name, ' TO') ? 'TO' : 'CC';
            });

        $differences = [
            'to_create' => [],
            'to_update' => [],
            'in_sync' => []
        ];

        foreach (['TO', 'CC'] as $folderType) {
            $expectedName = $expectedFolders[$folderType];

            if (!$existingFolders->has($folderType)) {
                // Folder doesn't exist, needs to be created
                $differences['to_create'][] = [
                    'type' => $folderType,
                    'expected_name' => $expectedName,
                    'model_id' => $model->id,
                    'model_type' => $modelClass
                ];
            } else {
                $existingFolder = $existingFolders[$folderType];
                $sanitizedCurrentName = MicrosoftGraphUtilities::sanitizeFolderDisplayName($existingFolder->display_name);

                if ($sanitizedCurrentName !== $expectedName) {
                    // Folder exists but name has changed, needs update
                    $differences['to_update'][] = [
                        'type' => $folderType,
                        'folder_id' => $existingFolder->id,
                        'ms_folder_id' => $existingFolder->ms_folder_id,
                        'current_name' => $existingFolder->display_name,
                        'expected_name' => $expectedName,
                        'microsoft_user_id' => $existingFolder->microsoft_user_id
                    ];
                } else {
                    // Folder is in sync
                    $differences['in_sync'][] = [
                        'type' => $folderType,
                        'folder_id' => $existingFolder->id,
                        'name' => $expectedName
                    ];
                }
            }
        }

        Log::info('Folder comparison completed for model', [
            'model_type' => $modelType,
            'model_id' => $model->id,
            'model_name' => $modelName,
            'to_create' => count($differences['to_create']),
            'to_update' => count($differences['to_update']),
            'in_sync' => count($differences['in_sync'])
        ]);

        return $differences;
    }

    /**
     * Display folder differences in a formatted table
     */
    protected function displayFolderDifferences(array $differences, $model, string $modelType): void
    {
        $toCreateCount = count($differences['to_create']);
        $toUpdateCount = count($differences['to_update']);
        $inSyncCount = count($differences['in_sync']);

        $this->table(
            ['Status', 'Count', 'Description'],
            [
                ['✅ In Sync', $inSyncCount, 'Folders that match expected names'],
                ['➕ To Create', $toCreateCount, 'Missing folders to create in Outlook'],
                ['📝 To Update', $toUpdateCount, 'Folders with outdated names']
            ]
        );

        if ($toCreateCount > 0) {
            $this->warn("   Folders to create:");
            foreach ($differences['to_create'] as $folder) {
                $this->line("     • {$folder['expected_name']} ({$folder['type']})");
            }
        }

        if ($toUpdateCount > 0) {
            $this->warn("   Folders to update:");
            foreach ($differences['to_update'] as $folder) {
                $this->line("     • '{$folder['current_name']}' → '{$folder['expected_name']}'");
            }
        }
    }

    /**
     * Apply folder synchronization changes
     */
    protected function applyFolderSyncChanges($model, string $modelType, array $differences, MicrosoftGraphApiClient $apiClient, bool $dryRun, ?int $microsoftUserId = null): array
    {
        $results = [
            'created' => 0,
            'updated' => 0,
            'errors' => []
        ];

        // Create missing folders
        foreach ($differences['to_create'] as $folderData) {
            try {
                if (!$dryRun) {
                    $this->createContactFolder($folderData, $apiClient, $microsoftUserId);
                }
                $results['created']++;

                if ($dryRun) {
                    $this->line("     🧪 Would create: {$folderData['expected_name']}");
                } else {
                    $this->line("     ✅ Created: {$folderData['expected_name']}");
                }

            } catch (\Exception $e) {
                $error = "Failed to create folder {$folderData['expected_name']}: " . $e->getMessage();
                $results['errors'][] = $error;
                $this->line("     ❌ Error creating {$folderData['expected_name']}: " . $e->getMessage());
            }
        }

        // Update existing folders
        foreach ($differences['to_update'] as $folderData) {
            try {
                if (!$dryRun) {
                    $this->updateContactFolder($folderData, $apiClient);
                }
                $results['updated']++;

                if ($dryRun) {
                    $this->line("     🧪 Would update: '{$folderData['current_name']}' → '{$folderData['expected_name']}'");
                } else {
                    $this->line("     ✅ Updated: '{$folderData['current_name']}' → '{$folderData['expected_name']}'");
                }

            } catch (\Exception $e) {
                $error = "Failed to update folder {$folderData['current_name']}: " . $e->getMessage();
                $results['errors'][] = $error;
                $this->line("     ❌ Error updating {$folderData['current_name']}: " . $e->getMessage());
            }
        }

        return $results;
    }

    /**
     * Create a new contact folder in Outlook and save to database
     */
    protected function createContactFolder(array $folderData, MicrosoftGraphApiClient $apiClient, int $microsoftUserId): void
    {
        $microsoftUser = MicrosoftUser::find($microsoftUserId);
        if (!$microsoftUser) {
            throw new \Exception("Microsoft User with ID {$microsoftUserId} not found");
        }

        // Check if folder already exists for this Microsoft User
        $existingFolder = ContactFolder::where('microsoft_user_id', $microsoftUser->id)
            ->where('folderable_type', $folderData['model_type'])
            ->where('folderable_id', $folderData['model_id'])
            ->where(function($query) use ($folderData) {
                if (str_contains($folderData['expected_name'], ' TO')) {
                    $query->where('display_name', 'like', '% TO');
                } else {
                    $query->where('display_name', 'like', '% CC');
                }
            })
            ->first();

        if ($existingFolder) {
            Log::info('Contact folder already exists for Microsoft User', [
                'name' => $folderData['expected_name'],
                'microsoft_user_id' => $microsoftUser->id,
                'existing_folder_id' => $existingFolder->id
            ]);
            return; // Skip creation if already exists
        }

        // Create folder in Outlook
        $folderData_outlook = [
            'displayName' => $folderData['expected_name']
        ];

        $response = $apiClient->postWithRetry(
            "users/{$microsoftUser->email}/contactFolders",
            $folderData_outlook,
            ['operation' => 'create_contact_folder']
        );

        // Extract the ID from response
        $folderId = null;
        if (is_array($response) && isset($response['id'])) {
            $folderId = $response['id'];
        } elseif (is_object($response) && isset($response->id)) {
            $folderId = $response->id;
        }

        if (!$folderId) {
            throw new \Exception('Failed to get folder ID from Outlook response');
        }

        // Save to our database
        ContactFolder::create([
            'microsoft_user_id' => $microsoftUser->id,
            'ms_folder_id' => $folderId,
            'display_name' => $folderData['expected_name'],
            'folderable_type' => $folderData['model_type'],
            'folderable_id' => $folderData['model_id']
        ]);

        Log::info('Contact folder created in Outlook and database', [
            'name' => $folderData['expected_name'],
            'ms_folder_id' => $folderId,
            'microsoft_user_id' => $microsoftUser->id,
            'model_type' => $folderData['model_type'],
            'model_id' => $folderData['model_id']
        ]);

        Log::info('Contact folder created in Outlook and database', [
            'name' => $folderData['expected_name'],
            'ms_folder_id' => $folderId,
            'microsoft_user_id' => $microsoftUser->id,
            'model_type' => $folderData['model_type'],
            'model_id' => $folderData['model_id']
        ]);
    }

    /**
     * Update an existing contact folder in Outlook and database
     */
    protected function updateContactFolder(array $folderData, MicrosoftGraphApiClient $apiClient): void
    {
        $microsoftUser = \App\Models\MicrosoftUser::find($folderData['microsoft_user_id']);
        if (!$microsoftUser) {
            throw new \Exception("Microsoft User with ID {$folderData['microsoft_user_id']} not found");
        }

        // Update folder in Outlook
        $updateData = [
            'displayName' => $folderData['expected_name']
        ];

        $apiClient->patchWithRetry(
            "users/{$microsoftUser->email}/contactFolders/{$folderData['ms_folder_id']}",
            $updateData,
            ['operation' => 'update_contact_folder']
        );

        // Update in our database
        ContactFolder::where('id', $folderData['folder_id'])->update([
            'display_name' => $folderData['expected_name']
        ]);

        Log::info('Contact folder updated in Outlook and database', [
            'folder_id' => $folderData['folder_id'],
            'ms_folder_id' => $folderData['ms_folder_id'],
            'old_name' => $folderData['current_name'],
            'new_name' => $folderData['expected_name']
        ]);
    }

    /**
     * Handle folder synchronization with detailed statistics tracking
     */
    protected function handleFolderSyncWithStats(bool $dryRun, array &$syncStats): int
    {
        try {
            $microsoftUserId = $this->option('microsoft-user-id');

            // Get target Microsoft Users
            if ($microsoftUserId) {
                $targetMicrosoftUsers = collect([MicrosoftUser::find($microsoftUserId)]);
                if ($targetMicrosoftUsers->first() === null) {
                    $this->error("❌ Microsoft User with ID {$microsoftUserId} not found");
                    return 1;
                }
            } else {
                $targetMicrosoftUsers = MicrosoftUser::where('is_new', 0)->get();
                if ($targetMicrosoftUsers->isEmpty()) {
                    $this->error('❌ No active Microsoft Users found (is_new = 0)');
                    return 1;
                }
            }

            $this->info("👥 Processing " . $targetMicrosoftUsers->count() . " Microsoft User(s)");
            $this->newLine();

            $apiClient = app(MicrosoftGraphApiClient::class);
            $apiClient->initialize();

            // Process each Microsoft User
            foreach ($targetMicrosoftUsers as $microsoftUser) {
                $this->info("🔄 Processing Microsoft User: {$microsoftUser->name} ({$microsoftUser->email})");

                $userResults = $this->syncFoldersForMicrosoftUser($microsoftUser, $apiClient, $dryRun);

                // Update sync statistics
                $syncStats['folder_sync']['users_processed']++;
                $syncStats['folder_sync']['models_processed'] += $userResults['models_processed'];
                $syncStats['folder_sync']['folders_created'] += $userResults['folders_created'];
                $syncStats['folder_sync']['folders_updated'] += $userResults['folders_updated'];
                $syncStats['folder_sync']['folders_deleted'] += $userResults['folders_deleted'];
                $syncStats['folder_sync']['errors'] = array_merge($syncStats['folder_sync']['errors'], $userResults['errors']);

                $this->info("   ✅ Models: {$userResults['models_processed']}, Created: {$userResults['folders_created']}, Updated: {$userResults['folders_updated']}, Deleted: {$userResults['folders_deleted']}");
                if (!empty($userResults['errors'])) {
                    $this->warn("   ⚠️  Errors: " . count($userResults['errors']));
                }
                $this->newLine();
            }

            return count($syncStats['folder_sync']['errors']) > 0 ? 1 : 0;

        } catch (\Exception $e) {
            $syncStats['folder_sync']['errors'][] = 'Critical error: ' . $e->getMessage();
            $this->error('❌ Error during folder synchronization: ' . $e->getMessage());
            return 1;
        }
    }

    /**
     * Handle contact synchronization with detailed statistics tracking
     */
    protected function handleContactSyncWithStats(bool $dryRun, array &$syncStats): int
    {
        try {
            $targetFolders = $this->getTargetContactFolders();

            if ($targetFolders->isEmpty()) {
                $this->error('❌ No contact folders found matching the criteria');
                return 1;
            }

            $this->info("📁 Found {$targetFolders->count()} contact folder(s) to synchronize");
            $this->newLine();

            $apiClient = app(MicrosoftGraphApiClient::class);
            $apiClient->initialize();

            foreach ($targetFolders as $folder) {
                $this->info("🔄 Synchronizing folder: {$folder->display_name}");
                $this->info("   Microsoft User: {$folder->microsoftUser->name} ({$folder->microsoftUser->email})");

                $differences = $this->compareContactsInFolder($folder, $apiClient);
                $folderResults = $this->applySyncChanges($folder, $differences, $apiClient, $dryRun);

                // Update sync statistics
                $syncStats['contact_sync']['folders_processed']++;
                $syncStats['contact_sync']['contacts_added'] += $folderResults['added'];
                $syncStats['contact_sync']['contacts_removed'] += $folderResults['removed'];
                $syncStats['contact_sync']['contacts_repaired'] += $folderResults['repaired'] ?? 0;
                $syncStats['contact_sync']['errors'] = array_merge($syncStats['contact_sync']['errors'], $folderResults['errors']);

                $this->info("   ✅ Added: {$folderResults['added']}, Removed: {$folderResults['removed']}, Repaired: " . ($folderResults['repaired'] ?? 0));
                if (!empty($folderResults['errors'])) {
                    $this->warn("   ⚠️  Errors: " . count($folderResults['errors']));
                }

                $this->newLine();
            }

            return count($syncStats['contact_sync']['errors']) > 0 ? 1 : 0;

        } catch (\Exception $e) {
            $syncStats['contact_sync']['errors'][] = 'Critical error: ' . $e->getMessage();
            $this->error('❌ Error during contact synchronization: ' . $e->getMessage());
            return 1;
        }
    }

    /**
     * Send email report if requested via command options
     */
    protected function sendEmailReportIfRequested(array $syncStats): void
    {
        $emailReport = $this->option('email-report');
        $emailOnErrors = $this->option('email-on-errors');

        // Determine if we should send email and get recipient emails
        $shouldSendEmail = false;
        $recipientEmails = [];

        if ($emailReport) {
            $shouldSendEmail = true;
            $recipientEmails = $this->parseEmailAddresses($emailReport);
        } elseif ($emailOnErrors !== null && ($syncStats['overall_status'] === 'errors' || $syncStats['overall_status'] === 'critical_error')) {
            $shouldSendEmail = true;
            // Check if --email-on-errors has specific emails provided
            if (!empty($emailOnErrors)) {
                $recipientEmails = $this->parseEmailAddresses($emailOnErrors);
            } else {
                // Use default admin email from config when --email-on-errors is used without value
                $defaultEmail = config('app.admin_email', 'christian.freitas@eae.pt');
                $recipientEmails = [$defaultEmail];
            }
        }

        if ($shouldSendEmail && !empty($recipientEmails)) {
            $successCount = 0;
            $failureCount = 0;

            foreach ($recipientEmails as $email) {
                try {
                    $this->sendSyncReportEmail($email, $syncStats);
                    $successCount++;
                } catch (\Exception $e) {
                    $failureCount++;
                    $this->warn("⚠️  Failed to send email report to {$email}: " . $e->getMessage());
                    Log::warning('Failed to send sync report email', [
                        'recipient' => $email,
                        'error' => $e->getMessage()
                    ]);
                }
            }

            if ($successCount > 0) {
                $emailList = implode(', ', array_slice($recipientEmails, 0, 3));
                if (count($recipientEmails) > 3) {
                    $emailList .= " and " . (count($recipientEmails) - 3) . " more";
                }
                $this->info("📧 Email report sent successfully to {$successCount} recipient(s): {$emailList}");
            }

            if ($failureCount > 0) {
                $this->warn("⚠️  Failed to send email to {$failureCount} recipient(s)");
            }
        }
    }

    /**
     * Parse email addresses from comma-separated string
     */
    protected function parseEmailAddresses(string $emailString): array
    {
        // Split by comma and clean up each email
        $emails = array_map('trim', explode(',', $emailString));

        // Filter out empty emails and validate basic format
        $validEmails = [];
        foreach ($emails as $email) {
            if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
                $validEmails[] = $email;
            } elseif (!empty($email)) {
                $this->warn("⚠️  Invalid email address format: {$email}");
                Log::warning('Invalid email address provided', ['email' => $email]);
            }
        }

        return array_unique($validEmails);
    }

    /**
     * Send detailed email report with sync statistics
     */
    protected function sendSyncReportEmail(string $recipientEmail, array $syncStats): void
    {
        $subject = $this->generateEmailSubject($syncStats);
        $htmlContent = $this->generateEmailHtmlContent($syncStats);
        $textContent = $this->generateEmailTextContent($syncStats);

        Mail::send([], [], function ($message) use ($recipientEmail, $subject, $htmlContent, $textContent) {
            $message->to($recipientEmail)
                ->subject($subject)
                ->setBody($htmlContent, 'text/html')
                ->addPart($textContent, 'text/plain');
        });

        Log::info('Sync report email sent', [
            'recipient' => $recipientEmail,
            'subject' => $subject,
            'overall_status' => $syncStats['overall_status'],
            'execution_time' => $syncStats['execution_time'] ?? 'unknown'
        ]);
    }

    /**
     * Generate email subject based on sync results
     */
    protected function generateEmailSubject(array $syncStats): string
    {
        $status = $syncStats['overall_status'];
        $mode = $syncStats['dry_run'] ? ' (DRY RUN)' : '';

        switch ($status) {
            case 'success':
                return "✅ Microsoft Graph Sync Completed Successfully{$mode}";
            case 'errors':
                return "⚠️  Microsoft Graph Sync Completed with Errors{$mode}";
            case 'critical_error':
                return "❌ Microsoft Graph Sync Failed{$mode}";
            default:
                return "📊 Microsoft Graph Sync Report{$mode}";
        }
    }

    /**
     * Generate HTML email content
     */
    protected function generateEmailHtmlContent(array $syncStats): string
    {
        $status = $syncStats['overall_status'];
        $statusIcon = match($status) {
            'success' => '✅',
            'errors' => '⚠️',
            'critical_error' => '❌',
            default => '📊'
        };

        $statusColor = match($status) {
            'success' => '#28a745',
            'errors' => '#ffc107',
            'critical_error' => '#dc3545',
            default => '#6c757d'
        };

        $html = "
        <html>
        <head>
            <style>
                body { font-family: Arial, sans-serif; margin: 20px; }
                .header { background-color: {$statusColor}; color: white; padding: 15px; border-radius: 5px; }
                .section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
                .success { background-color: #d4edda; border-color: #c3e6cb; }
                .warning { background-color: #fff3cd; border-color: #ffeaa7; }
                .error { background-color: #f8d7da; border-color: #f5c6cb; }
                .stats-table { width: 100%; border-collapse: collapse; margin: 10px 0; }
                .stats-table th, .stats-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
                .stats-table th { background-color: #f8f9fa; }
                .error-list { margin-top: 10px; }
                .error-item { background-color: #f8d7da; padding: 5px; margin: 2px 0; border-radius: 3px; }
            </style>
        </head>
        <body>
            <div class='header'>
                <h1>{$statusIcon} Microsoft Graph Smart Sync Report</h1>
                <p>Status: {$status} | Duration: {$syncStats['execution_time']} seconds</p>
                <p>Executed: {$syncStats['start_time']} - {$syncStats['end_time']}</p>";

        if ($syncStats['dry_run']) {
            $html .= "<p><strong>🧪 DRY RUN MODE - No actual changes were made</strong></p>";
        }

        $html .= "
            </div>

            <div class='section'>
                <h2>📁 Folder Synchronization Results</h2>
                <table class='stats-table'>
                    <tr><th>Metric</th><th>Count</th></tr>
                    <tr><td>Microsoft Users Processed</td><td>{$syncStats['folder_sync']['users_processed']}</td></tr>
                    <tr><td>Models Processed</td><td>{$syncStats['folder_sync']['models_processed']}</td></tr>
                    <tr><td>Folders Created</td><td>{$syncStats['folder_sync']['folders_created']}</td></tr>
                    <tr><td>Folders Updated</td><td>{$syncStats['folder_sync']['folders_updated']}</td></tr>
                    <tr><td>Folders Deleted (Orphaned)</td><td>{$syncStats['folder_sync']['folders_deleted']}</td></tr>
                    <tr><td>Errors</td><td>" . count($syncStats['folder_sync']['errors']) . "</td></tr>
                </table>";

        if (!empty($syncStats['folder_sync']['errors'])) {
            $html .= "<div class='error-list'><h3>Folder Sync Errors:</h3>";
            foreach ($syncStats['folder_sync']['errors'] as $error) {
                $html .= "<div class='error-item'>{$error}</div>";
            }
            $html .= "</div>";
        }

        $html .= "
            </div>

            <div class='section'>
                <h2>📧 Contact Synchronization Results</h2>
                <table class='stats-table'>
                    <tr><th>Metric</th><th>Count</th></tr>
                    <tr><td>Contact Folders Processed</td><td>{$syncStats['contact_sync']['folders_processed']}</td></tr>
                    <tr><td>Contacts Added</td><td>{$syncStats['contact_sync']['contacts_added']}</td></tr>
                    <tr><td>Contacts Removed</td><td>{$syncStats['contact_sync']['contacts_removed']}</td></tr>
                    <tr><td>Contacts Repaired</td><td>{$syncStats['contact_sync']['contacts_repaired']}</td></tr>
                    <tr><td>Errors</td><td>" . count($syncStats['contact_sync']['errors']) . "</td></tr>
                </table>";

        if (!empty($syncStats['contact_sync']['errors'])) {
            $html .= "<div class='error-list'><h3>Contact Sync Errors:</h3>";
            foreach ($syncStats['contact_sync']['errors'] as $error) {
                $html .= "<div class='error-item'>{$error}</div>";
            }
            $html .= "</div>";
        }

        $html .= "</div>";

        if (isset($syncStats['critical_error'])) {
            $html .= "
            <div class='section error'>
                <h2>❌ Critical Error</h2>
                <p>{$syncStats['critical_error']}</p>
            </div>";
        }

        $html .= "
        </body>
        </html>";

        return $html;
    }

    /**
     * Generate plain text email content
     */
    protected function generateEmailTextContent(array $syncStats): string
    {
        $status = $syncStats['overall_status'];
        $statusIcon = match($status) {
            'success' => '✅',
            'errors' => '⚠️',
            'critical_error' => '❌',
            default => '📊'
        };

        $text = "{$statusIcon} MICROSOFT GRAPH SMART SYNC REPORT\n";
        $text .= str_repeat("=", 50) . "\n\n";
        $text .= "Status: {$status}\n";
        $text .= "Duration: {$syncStats['execution_time']} seconds\n";
        $text .= "Executed: {$syncStats['start_time']} - {$syncStats['end_time']}\n";

        if ($syncStats['dry_run']) {
            $text .= "🧪 DRY RUN MODE - No actual changes were made\n";
        }

        $text .= "\n📁 FOLDER SYNCHRONIZATION RESULTS:\n";
        $text .= "• Microsoft Users Processed: {$syncStats['folder_sync']['users_processed']}\n";
        $text .= "• Models Processed: {$syncStats['folder_sync']['models_processed']}\n";
        $text .= "• Folders Created: {$syncStats['folder_sync']['folders_created']}\n";
        $text .= "• Folders Updated: {$syncStats['folder_sync']['folders_updated']}\n";
        $text .= "• Folders Deleted (Orphaned): {$syncStats['folder_sync']['folders_deleted']}\n";
        $text .= "• Errors: " . count($syncStats['folder_sync']['errors']) . "\n";

        if (!empty($syncStats['folder_sync']['errors'])) {
            $text .= "\nFolder Sync Errors:\n";
            foreach ($syncStats['folder_sync']['errors'] as $error) {
                $text .= "  - {$error}\n";
            }
        }

        $text .= "\n📧 CONTACT SYNCHRONIZATION RESULTS:\n";
        $text .= "• Contact Folders Processed: {$syncStats['contact_sync']['folders_processed']}\n";
        $text .= "• Contacts Added: {$syncStats['contact_sync']['contacts_added']}\n";
        $text .= "• Contacts Removed: {$syncStats['contact_sync']['contacts_removed']}\n";
        $text .= "• Contacts Repaired: {$syncStats['contact_sync']['contacts_repaired']}\n";
        $text .= "• Errors: " . count($syncStats['contact_sync']['errors']) . "\n";

        if (!empty($syncStats['contact_sync']['errors'])) {
            $text .= "\nContact Sync Errors:\n";
            foreach ($syncStats['contact_sync']['errors'] as $error) {
                $text .= "  - {$error}\n";
            }
        }

        if (isset($syncStats['critical_error'])) {
            $text .= "\n❌ CRITICAL ERROR:\n";
            $text .= $syncStats['critical_error'] . "\n";
        }

        return $text;
    }
}
