<?php

namespace App\Services\MicrosoftGraph;

use App\Services\MicrosoftGraph\MicrosoftGraphApiClient;
use App\Services\MicrosoftGraph\MicrosoftGraphContactFolderService;
use App\Services\MicrosoftGraph\MicrosoftGraphContactService;
use App\Services\MicrosoftGraph\MicrosoftGraphTestService;
use App\Services\MicrosoftGraph\MicrosoftGraphUtilities;
use App\Models\ContactFolder;
use App\Models\Contact;
use App\Models\MicrosoftUser;
use Illuminate\Support\Facades\Log;
use Exception;

/**
 * Main orchestrator for Microsoft Graph synchronization operations
 * Coordinates between different services and manages the overall sync workflow
 */
class MicrosoftGraphSyncOrchestrator
{
    protected $apiClient;
    protected $folderService;
    protected $contactService;
    protected $testService;

    public function __construct()
    {
        $this->apiClient = new MicrosoftGraphApiClient();
        $this->folderService = new MicrosoftGraphContactFolderService($this->apiClient);
        $this->contactService = new MicrosoftGraphContactService($this->apiClient);
        $this->testService = new MicrosoftGraphTestService($this->apiClient);
    }

    /**
     * Initialize all services
     */
    public function initialize(): self
    {
        $this->apiClient->initialize();
        return $this;
    }

    /**
     * Complete synchronization for new users only
     * 1. Process only MicrosoftUsers with is_new = 1
     * 2. For each user, delete all their contact folders (which also deletes contacts)
     * 3. Recreate folders and contacts from database for each user
     * 4. Mark processed users as is_new = 0
     */
    public function syncAll(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);

        MicrosoftGraphUtilities::logOperation('sync', 'Starting Microsoft Graph synchronization for new users only', [
            'dry_run' => $dryRun
        ]);

        $overallStats = [
            'operation' => 'syncAll',
            'dry_run' => $dryRun,
            'started_at' => now()->toISOString(),
            'new_users_found' => 0,
            'users_processed' => 0,
            'folders_deleted' => 0,
            'folders_created' => 0,
            'contacts_created' => 0,
            'users_updated' => 0,
            'errors' => [],
            'success' => false
        ];

        try {
            // Get all MicrosoftUsers with is_new = 1
            $newUsers = MicrosoftUser::whereNotNull('ms_id')
                                ->where('ms_id', '!=', '')
                                ->where('is_new', true)
                                ->get();

            $overallStats['new_users_found'] = $newUsers->count();

            MicrosoftGraphUtilities::logOperation('info', 'Found new users to process', [
                'count' => $newUsers->count(),
                'user_emails' => $newUsers->pluck('email')->toArray()
            ]);

            if ($newUsers->isEmpty()) {
                MicrosoftGraphUtilities::logOperation('info', 'No new users to process');
                $overallStats['success'] = true;
                $overallStats['completed_at'] = now()->toISOString();
                return $overallStats;
            }

            // Step 1: Delete all contact folders for ALL new users
            MicrosoftGraphUtilities::logOperation('info', "Deleting all contact folders for new users");
            foreach ($newUsers as $user) {
                try {
                    MicrosoftGraphUtilities::logOperation('info', "Deleting contact folders for user: {$user->email}", [
                        'user_id' => $user->id,
                        'ms_id' => $user->ms_id
                    ]);

                    $foldersToDelete = ContactFolder::where('microsoft_user_id', $user->id)->get();

                    foreach ($foldersToDelete as $folder) {
                        try {
                            if ($folder->ms_folder_id && !$dryRun) {
                                // Permanent delete from Microsoft Graph
                                $this->apiClient->postWithRetry(
                                    "/users/{$user->ms_id}/contactFolders/{$folder->ms_folder_id}/permanentDelete",
                                    [],
                                    [
                                        'user_email' => $user->email,
                                        'folder_name' => $folder->display_name,
                                        'operation' => 'permanent_delete_folder'
                                    ]
                                );
                            }

                            // Delete from local database
                            if (!$dryRun) {
                                $folder->delete();
                            }

                            $overallStats['folders_deleted']++;

                        } catch (Exception $e) {
                            $overallStats['errors'][] = "Error deleting folder {$folder->display_name} for user {$user->email}: " . $e->getMessage();
                        }
                    }

                    $overallStats['users_processed']++;

                } catch (Exception $e) {
                    $overallStats['errors'][] = "Failed to delete folders for user {$user->email}: " . $e->getMessage();

                    MicrosoftGraphUtilities::logOperation('error', "Failed to delete folders for user {$user->email}", [
                        'error' => $e->getMessage(),
                        'user_id' => $user->id
                    ]);
                }
            }

            // Step 2: Recreate contact folders for ALL new users (executed only once)
            MicrosoftGraphUtilities::logOperation('info', "Recreating contact folders for all new users");
            $folderStats = $this->folderService->syncContactFolders($dryRun);
            $overallStats['folders_created'] += ($folderStats['created'] ?? 0) + ($folderStats['updated'] ?? 0);

            // Step 3: Recreate contacts for new users only (executed only once)
            MicrosoftGraphUtilities::logOperation('info', "Recreating contacts for new users contact folders only");
            $contactStats = $this->contactService->syncContacts($dryRun, true); // true = onlyNewUsers
            $overallStats['contacts_created'] += $contactStats['created'] ?? 0;

            // Step 4: Mark ALL new users as processed (is_new = 0)
            if (!$dryRun) {
                $updatedCount = MicrosoftUser::whereNotNull('ms_id')
                                    ->where('ms_id', '!=', '')
                                    ->where('is_new', true)
                                    ->update(['is_new' => false]);

                $overallStats['users_updated'] = $updatedCount;
                MicrosoftGraphUtilities::logOperation('info', "Marked {$updatedCount} users as processed (is_new = 0)");
            } else {
                $overallStats['users_updated'] = $newUsers->count();
                MicrosoftGraphUtilities::logOperation('info', "DRY RUN: Would mark {$newUsers->count()} users as processed");
            }

            $overallStats['success'] = empty($overallStats['errors']);
            $overallStats['completed_at'] = now()->toISOString();

        } catch (Exception $e) {
            $overallStats['errors'][] = 'Sync operation failed: ' . $e->getMessage();
            $overallStats['success'] = false;
            $overallStats['completed_at'] = now()->toISOString();

            MicrosoftGraphUtilities::logOperation('error', 'Complete sync failed', [
                'error' => $e->getMessage()
            ]);
        }

        MicrosoftGraphUtilities::logOperation(
            $overallStats['success'] ? 'success' : 'error',
            'Microsoft Graph synchronization for new users finished',
            $overallStats
        );

        return $overallStats;
    }

    /**
     * Sync only contact folders
     */
    public function syncContactFolders(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);
        return $this->folderService->syncContactFolders($dryRun);
    }

    /**
     * Sync only contacts
     */
    public function syncContacts(bool $dryRun = false, bool $onlyNewUsers = false): array
    {
        $this->apiClient->setDryRun($dryRun);
        return $this->contactService->syncContacts($dryRun, $onlyNewUsers);
    }

    /**
     * Delete all contact folders and contacts (clean slate)
     */
    public function deleteAllContactFolders(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);

        MicrosoftGraphUtilities::logOperation('info', 'Starting clean slate deletion', [
            'dry_run' => $dryRun
        ]);

        $overallStats = [
            'operation' => 'deleteAll',
            'dry_run' => $dryRun,
            'started_at' => now()->toISOString(),
            'contacts_deleted' => 0,
            'folders_deleted' => 0,
            'errors' => [],
            'success' => false
        ];

        try {
            // Step 1: Delete all contacts
            // MicrosoftGraphUtilities::logOperation('info', 'Step 1: Deleting all contacts...');
            // $contactStats = $this->contactService->deleteAllContacts($dryRun);
            // $overallStats['contacts_deleted'] = $contactStats['contacts_deleted'];

            // Step 2: Delete all folders
            MicrosoftGraphUtilities::logOperation('info', 'Step 2: Deleting all contact folders...');
            $folderStats = $this->folderService->deleteAllContactFolders($dryRun);
            $overallStats['folders_deleted'] = $folderStats['folders_deleted'];

            // Combine errors
            $overallStats['errors'] = array_merge(
                $contactStats['errors'] ?? [],
                $folderStats['errors'] ?? []
            );

            $overallStats['success'] = empty($overallStats['errors']);
            $overallStats['completed_at'] = now()->toISOString();

        } catch (Exception $e) {
            $overallStats['errors'][] = 'Delete operation failed: ' . $e->getMessage();
            $overallStats['success'] = false;
            $overallStats['completed_at'] = now()->toISOString();

            MicrosoftGraphUtilities::logOperation('error', 'Clean slate deletion failed', [
                'error' => $e->getMessage()
            ]);
        }

        MicrosoftGraphUtilities::logOperation(
            $overallStats['success'] ? 'success' : 'error',
            'Clean slate deletion completed',
            $overallStats
        );

        return $overallStats;
    }

    /**
     * Get comprehensive statistics
     */
    public function getStats(): array
    {
        return $this->testService->getStats();
    }

    /**
     * Test Microsoft Graph connection
     */
    public function testConnection(): array
    {
        return $this->testService->testConnection();
    }

    /**
     * Run comprehensive diagnostics
     */
    public function runDiagnostics(): array
    {
        return $this->testService->runDiagnostics();
    }

    /**
     * List all contact folders
     */
    public function listContactFolders(string $userEmail = null, bool $withContactCount = false): array
    {
        try {
            $result = $this->folderService->listContactFolders($userEmail, $withContactCount);

            // Handle new response format with multiple service users and folders grouped by user
            if (isset($result['folders_by_user']) && is_array($result['folders_by_user'])) {
                // New format with folders grouped by user
                return [
                    'status' => 'success',
                    'total_folders' => $result['total_folders'] ?? 0,
                    'total_users' => $result['total_users'] ?? 0,
                    'folders' => $result['folders'] ?? [], // All folders combined (backward compatibility)
                    'folders_by_user' => $result['folders_by_user'], // Folders grouped by user
                    'service_users' => $result['service_users'] ?? [],
                    'errors' => $result['errors'] ?? []
                ];
            } elseif (isset($result['service_users']) && is_array($result['service_users'])) {
                // Previous format with multiple service users but folders not grouped
                return [
                    'status' => 'success',
                    'total_folders' => $result['total_folders'] ?? 0,
                    'total_users' => $result['total_users'] ?? 0,
                    'folders' => $result['folders'] ?? [],
                    'folders_by_user' => [], // Empty since not grouped
                    'service_users' => $result['service_users'],
                    'errors' => $result['errors'] ?? []
                ];
            } elseif (isset($result['folders'])) {
                // Legacy format with single service user
                return [
                    'status' => 'success',
                    'total_folders' => count($result['folders']),
                    'total_users' => $result['service_user'] ? 1 : 0,
                    'folders' => $result['folders'],
                    'folders_by_user' => $result['service_user'] ? [
                        [
                            'user' => $result['service_user'],
                            'folders' => $result['folders'],
                            'folder_count' => count($result['folders'])
                        ]
                    ] : [],
                    'service_users' => $result['service_user'] ? [$result['service_user']] : [],
                    'service_user' => $result['service_user'] ?? null, // Backward compatibility
                    'user_email' => $result['user_email'] ?? null, // Backward compatibility
                    'errors' => []
                ];
            } else {
                // Very old format (backward compatibility)
                return [
                    'status' => 'success',
                    'total_folders' => count($result),
                    'total_users' => 0,
                    'folders' => $result,
                    'folders_by_user' => [],
                    'service_users' => [],
                    'service_user' => null,
                    'user_email' => null,
                    'errors' => []
                ];
            }

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', 'Failed to list contact folders in orchestrator', [
                'error' => $e->getMessage()
            ]);

            return [
                'status' => 'error',
                'message' => $e->getMessage(),
                'total_folders' => 0,
                'total_users' => 0,
                'folders' => [],
                'folders_by_user' => [],
                'service_users' => [],
                'errors' => [$e->getMessage()]
            ];
        }
    }

    /**
     * List contacts in a specific folder
     */
    public function listContactsInFolder(string $folderIdOrName): array
    {
        try {
            MicrosoftGraphUtilities::logOperation('debug', 'Looking for folder', [
                'search_term' => $folderIdOrName
            ]);

            // First try to find folder by ID or name in database
            $contactFolder = ContactFolder::where('ms_folder_id', $folderIdOrName)
                ->orWhere('display_name', $folderIdOrName)
                ->first();

            if (!$contactFolder) {
                MicrosoftGraphUtilities::logOperation('error', 'Contact folder not found', [
                    'search_term' => $folderIdOrName,
                    'available_folders' => ContactFolder::pluck('display_name')->toArray()
                ]);
                throw new Exception("Contact folder not found: {$folderIdOrName}");
            }

            MicrosoftGraphUtilities::logOperation('debug', 'Found folder', [
                'folder_id' => $contactFolder->id,
                'ms_folder_id' => $contactFolder->ms_folder_id,
                'display_name' => $contactFolder->display_name,
                'user_email' => $contactFolder->microsoftUser->email
            ]);

            $contacts = $this->contactService->listContactsInFolder(
                $contactFolder->ms_folder_id,
                $contactFolder->microsoftUser->email
            );

            // Return directly the contacts array for command usage
            return $contacts;

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', 'Failed to list contacts from folder', [
                'folder' => $folderIdOrName,
                'error' => $e->getMessage()
            ]);
            // Return empty array for command usage
            return [];
        }
    }

    /**
     * List all contacts across all folders
     */
    public function listAllContacts(): array
    {
        try {
            $allContacts = [];
            $totalCount = 0;
            $folderCount = 0;

            $contactFolders = ContactFolder::whereNotNull('ms_folder_id')->get();

            foreach ($contactFolders as $contactFolder) {
                $folderCount++;
                $contacts = $this->contactService->listContactsInFolder(
                    $contactFolder->ms_folder_id,
                    $contactFolder->microsoftUser->email
                );

                $allContacts[] = [
                    'folder' => [
                        'id' => $contactFolder->ms_folder_id,
                        'name' => $contactFolder->display_name,
                        'type' => $contactFolder->folder_type
                    ],
                    'count' => count($contacts),
                    'contacts' => $contacts
                ];

                $totalCount += count($contacts);
            }

            return [
                'status' => 'success',
                'summary' => [
                    'folders' => $folderCount,
                    'total_contacts' => $totalCount
                ],
                'folders' => $allContacts
            ];

        } catch (Exception $e) {
            return [
                'status' => 'error',
                'message' => 'Failed to list all contacts: ' . $e->getMessage(),
                'summary' => ['folders' => 0, 'total_contacts' => 0],
                'folders' => []
            ];
        }
    }

    /**
     * Create a test contact folder
     */
    public function createTestContactFolder(string $folderName = 'Test-Contact-Folder'): array
    {
        return $this->testService->createTestContactFolder($folderName);
    }

    /**
     * Delete a test contact folder
     */
    public function deleteTestContactFolder(string $folderId): array
    {
        return $this->testService->deleteTestContactFolder($folderId);
    }

    /**
     * Clear all contacts but keep folders (useful for re-sync scenarios)
     */
    public function clearAllContacts(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);

        MicrosoftGraphUtilities::logOperation('info', 'Clearing all contacts (keeping folders)', [
            'dry_run' => $dryRun
        ]);

        $stats = $this->contactService->deleteAllContacts($dryRun);

        MicrosoftGraphUtilities::logOperation('success', 'All contacts cleared', $stats);

        return $stats;
    }

    /**
     * Force reconnection to Microsoft Graph
     */
    public function forceReconnection(): void
    {
        $this->apiClient->forceReconnection();
    }

    /**
     * Check if Microsoft Graph service is available
     */
    public function isAvailable(): bool
    {
        return MicrosoftGraphApiClient::isAvailable();
    }

    /**
     * Set dry run mode for all operations
     */
    public function setDryRun(bool $dryRun): self
    {
        $this->apiClient->setDryRun($dryRun);
        return $this;
    }

    /**
     * Get the current dry run status
     */
    public function isDryRun(): bool
    {
        return $this->apiClient->isDryRun();
    }

    /**
     * Delete all contact folders and contacts from Microsoft Graph AND clean local database
     * This is a DESTRUCTIVE operation that removes everything from both remote and local
     */
    public function deleteAllContactFoldersAndContacts(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);

        MicrosoftGraphUtilities::logOperation('warning', 'Starting COMPLETE CLEANUP - This will delete ALL contact folders and contacts from Microsoft Graph AND local database', [
            'dry_run' => $dryRun
        ]);

        $overallStats = [
            'operation' => 'deleteAllContactFoldersAndContacts',
            'dry_run' => $dryRun,
            'started_at' => now()->toISOString(),
            'msgraph_contacts_deleted' => 0,
            'msgraph_folders_deleted' => 0,
            'local_contacts_deleted' => 0,
            'local_folders_deleted' => 0,
            'errors' => [],
            'success' => false
        ];

        try {
            // Step 1: Delete everything from Microsoft Graph
            MicrosoftGraphUtilities::logOperation('info', 'Step 1: Deleting all from Microsoft Graph...');
            $remoteStats = $this->deleteAllContactFolders($dryRun);

            $overallStats['msgraph_contacts_deleted'] = $remoteStats['contacts_deleted'];
            $overallStats['msgraph_folders_deleted'] = $remoteStats['folders_deleted'];
            $overallStats['errors'] = array_merge($overallStats['errors'], $remoteStats['errors'] ?? []);

            // Step 2: Clean local database tables
            MicrosoftGraphUtilities::logOperation('info', 'Step 2: Cleaning local database tables...');

            if (!$dryRun) {
                // Delete all contacts first (due to foreign key constraints)
                $overallStats['local_contacts_deleted'] = Contact::count();
                Contact::query()->delete();
                MicrosoftGraphUtilities::logOperation('info', "Deleted {$overallStats['local_contacts_deleted']} contact records from local database");

                // Delete all contact folders
                $overallStats['local_folders_deleted'] = ContactFolder::count();
                ContactFolder::query()->delete();
                MicrosoftGraphUtilities::logOperation('info', "Deleted {$overallStats['local_folders_deleted']} contact folder records from local database");
            } else {
                // DRY RUN: Just count what would be deleted
                $overallStats['local_contacts_deleted'] = Contact::count();
                $overallStats['local_folders_deleted'] = ContactFolder::count();
                MicrosoftGraphUtilities::logOperation('info', "DRY RUN: Would delete {$overallStats['local_contacts_deleted']} contact records and {$overallStats['local_folders_deleted']} contact folder records from local database");
            }

            $overallStats['success'] = empty($overallStats['errors']);
            $overallStats['completed_at'] = now()->toISOString();

            // Summary
            $totalContacts = $overallStats['msgraph_contacts_deleted'] + $overallStats['local_contacts_deleted'];
            $totalFolders = $overallStats['msgraph_folders_deleted'] + $overallStats['local_folders_deleted'];

            MicrosoftGraphUtilities::logOperation(
                $overallStats['success'] ? 'success' : 'warning',
                'COMPLETE CLEANUP finished',
                [
                    'total_contacts_deleted' => $totalContacts,
                    'total_folders_deleted' => $totalFolders,
                    'errors_count' => count($overallStats['errors']),
                    'dry_run' => $dryRun
                ]
            );

        } catch (Exception $e) {
            $overallStats['errors'][] = 'Complete cleanup failed: ' . $e->getMessage();
            $overallStats['success'] = false;
            $overallStats['completed_at'] = now()->toISOString();

            MicrosoftGraphUtilities::logOperation('error', 'Complete cleanup failed', [
                'error' => $e->getMessage()
            ]);
        }

        return $overallStats;
    }
}
