<?php

namespace App\Services\MicrosoftGraph;

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

/**
 * Service for managing Microsoft Graph contact folders
 * Handles CRUD operations for contact folders
 */
class MicrosoftGraphContactFolderService
{
    protected $apiClient;

    public function __construct(MicrosoftGraphApiClient $apiClient)
    {
        $this->apiClient = $apiClient;
    }

    /**
     * Sync all contact folders for committees and workgroups across all service users
     */
    public function syncContactFolders(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);

        MicrosoftGraphUtilities::logOperation('sync', 'Starting contact folders synchronization', [
            'dry_run' => $dryRun
        ]);

        $stats = [
            'committees_processed' => 0,
            'workgroups_processed' => 0,
            'folders_created' => 0,
            'folders_updated' => 0,
            'folders_failed' => 0,
            'service_users_processed' => 0,
            'errors' => []
        ];

        try {
            // Get all service users for Microsoft Graph operations
            $serviceUsers = MicrosoftUser::whereNotNull('ms_id')
                                ->where('ms_id', '!=', '')
                                ->where('is_new', true)
                                ->get();

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

            MicrosoftGraphUtilities::logOperation('info', "Found {$serviceUsers->count()} service users");

            // Get published committees and workgroups once
            $publishedCommittees = $this->getPublishedCommittees();
            $publishedWorkgroups = $this->getPublishedWorkgroups();

            MicrosoftGraphUtilities::logOperation('info', "Found {$publishedCommittees->count()} published committees and {$publishedWorkgroups->count()} published workgroups");

            // Process each service user
            foreach ($serviceUsers as $serviceUser) {
                try {
                    $stats['service_users_processed']++;
                    MicrosoftGraphUtilities::logOperation('info', "Processing service user: {$serviceUser->email}");

                    // Sync committees for this service user
                    foreach ($publishedCommittees as $committee) {
                        $stats['committees_processed']++;
                        $this->syncModelFolders($committee, 'committee', $serviceUser, $stats, $dryRun);
                    }

                    // Sync workgroups for this service user
                    foreach ($publishedWorkgroups as $workgroup) {
                        $stats['workgroups_processed']++;
                        $this->syncModelFolders($workgroup, 'workgroup', $serviceUser, $stats, $dryRun);
                    }

                    MicrosoftGraphUtilities::logOperation('success', "Completed processing service user: {$serviceUser->email}");

                } catch (Exception $e) {
                    $stats['errors'][] = "Error processing service user {$serviceUser->email}: " . $e->getMessage();
                    MicrosoftGraphUtilities::logOperation('error', 'Failed to process service user', [
                        'user_email' => $serviceUser->email,
                        'error' => $e->getMessage()
                    ]);
                }
            }

        } catch (Exception $e) {
            $stats['errors'][] = $e->getMessage();
            MicrosoftGraphUtilities::logOperation('error', 'Contact folders sync failed', [
                'error' => $e->getMessage()
            ]);
        }

        MicrosoftGraphUtilities::logOperation('success', 'Contact folders synchronization completed', [
            'service_users_processed' => $stats['service_users_processed'],
            'committees_processed' => $stats['committees_processed'],
            'workgroups_processed' => $stats['workgroups_processed'],
            'folders_created' => $stats['folders_created'],
            'folders_updated' => $stats['folders_updated'],
            'folders_failed' => $stats['folders_failed'],
            'errors' => count($stats['errors'])
        ]);

        // Transform stats to expected format for command
        return [
            'created' => $stats['folders_created'],
            'updated' => $stats['folders_updated'],
            'errors' => count($stats['errors']),
            'failed' => $stats['folders_failed'],
            'committees_processed' => $stats['committees_processed'],
            'workgroups_processed' => $stats['workgroups_processed'],
            'service_users_processed' => $stats['service_users_processed'],
            'error_details' => $stats['errors']
        ];
    }

    /**
     * Sync folders for a specific model (committee or workgroup)
     */
    protected function syncModelFolders($model, string $modelType, MicrosoftUser $serviceUser, array &$stats, bool $dryRun): void
    {
        $modelTitle = MicrosoftGraphUtilities::getModelTitle($model);

        MicrosoftGraphUtilities::logOperation('sync', "Syncing folders for {$modelType}: {$modelTitle}");

        // Create/update both TO and CC folders
        foreach (['TO', 'CC'] as $folderType) {
            $folderId = $this->syncContactFolder($model, $modelType, $folderType, $serviceUser, $dryRun);

            if ($folderId) {
                $stats['folders_created']++;
            } else {
                $stats['folders_failed']++;
            }

            // Add throttling prevention
            $this->apiClient->preventThrottling('folder_creation', $stats['folders_created']);
        }
    }

    /**
     * Sync a specific contact folder for a model
     */
    protected function syncContactFolder($model, string $modelType, string $folderType, MicrosoftUser $serviceUser, bool $dryRun = false): ?string
    {
        try {
            $displayName = MicrosoftGraphUtilities::generateFolderDisplayName($model, $modelType, $folderType);
            $sanitizedDisplayName = MicrosoftGraphUtilities::sanitizeFolderDisplayName($displayName);

            MicrosoftGraphUtilities::logOperation('sync', "Syncing contact folder: {$displayName}");

            // Check if folder already exists in database
            $existingFolder = ContactFolder::where('folderable_type', $this->getModelClass($modelType))
                ->where('folderable_id', $model->id)
                ->where('microsoft_user_id', $serviceUser->id)
                ->where('display_name', 'LIKE', "%{$folderType}%")
                ->first();

            if ($existingFolder && $existingFolder->ms_folder_id) {
                MicrosoftGraphUtilities::logOperation('info', "Folder already exists: {$displayName}", [
                    'ms_folder_id' => $existingFolder->ms_folder_id
                ]);
                return $existingFolder->ms_folder_id;
            }

            // Create folder in Microsoft Graph with retry logic
            $folderData = [
                'displayName' => $sanitizedDisplayName
            ];

            $response = $this->apiClient->postWithRetry(
                "users/{$serviceUser->email}/contactFolders",
                $folderData,
                [
                    'operation' => 'create_folder',
                    'display_name' => $sanitizedDisplayName,
                    'model_type' => $modelType,
                    'folder_type' => $folderType
                ]
            );

            if (!$response) {
                throw new Exception("Failed to create contact folder: {$displayName}");
            }

            $msFolderId = $this->apiClient->getResponseValue($response, 'id');

            if (!$msFolderId) {
                throw new Exception("No folder ID returned from Microsoft Graph for: {$displayName}");
            }

            // Save or update folder in database
            if ($existingFolder) {
                $existingFolder->update([
                    'ms_folder_id' => $msFolderId,
                    'display_name' => $sanitizedDisplayName
                ]);
                MicrosoftGraphUtilities::logOperation('update', "Updated existing folder record: {$displayName}");
            } else {
                ContactFolder::create([
                    'folderable_type' => $this->getModelClass($modelType),
                    'folderable_id' => $model->id,
                    'microsoft_user_id' => $serviceUser->id,
                    'ms_folder_id' => $msFolderId,
                    'display_name' => $sanitizedDisplayName
                ]);
                MicrosoftGraphUtilities::logOperation('create', "Created new folder record: {$displayName}");
            }

            return $msFolderId;

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', "Failed to sync contact folder", [
                'model_type' => $modelType,
                'model_id' => $model->id,
                'folder_type' => $folderType,
                'error' => $e->getMessage()
            ]);

            // Handle specific error cases
            if (str_contains($e->getMessage(), 'ErrorFolderExists')) {
                return $this->handleExistingFolder($model, $modelType, $folderType, $serviceUser, $sanitizedDisplayName);
            }

            return null;
        }
    }

    /**
     * Handle the case where a folder already exists in Microsoft Graph
     */
    protected function handleExistingFolder($model, string $modelType, string $folderType, MicrosoftUser $serviceUser, string $displayName): ?string
    {
        try {
            MicrosoftGraphUtilities::logOperation('info', "Folder exists in Microsoft Graph, searching for ID: {$displayName}");

            // Search for the existing folder
            $folders = $this->listContactFolders($serviceUser->email);

            foreach ($folders as $folder) {
                $folderDisplayName = $this->apiClient->getResponseValue($folder, 'displayName');
                if ($folderDisplayName === $displayName) {
                    $msFolderId = $this->apiClient->getResponseValue($folder, 'id');

                    // Update database record
                    ContactFolder::updateOrCreate(
                        [
                            'folderable_type' => $this->getModelClass($modelType),
                            'folderable_id' => $model->id,
                            'microsoft_user_id' => $serviceUser->id,
                            'display_name' => $displayName
                        ],
                        [
                            'ms_folder_id' => $msFolderId
                        ]
                    );

                    MicrosoftGraphUtilities::logOperation('update', "Found and updated existing folder: {$displayName}");
                    return $msFolderId;
                }
            }

            MicrosoftGraphUtilities::logOperation('warning', "Could not find existing folder: {$displayName}");
            return null;

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', "Failed to handle existing folder", [
                'display_name' => $displayName,
                'error' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * List all contact folders for all service users or a specific user
     */
    public function listContactFolders(string $userEmail = null, bool $withContactCount = false): array
    {
        try {
            // If a specific user email is provided, get folders for that user only
            if ($userEmail) {
                return $this->listContactFoldersForUser($userEmail, $withContactCount);
            }

            // Get all service users
            $serviceUsers = MicrosoftUser::whereNotNull('ms_id')->where('ms_id', '!=', '')->get();
            if ($serviceUsers->isEmpty()) {
                throw new Exception('No service users found');
            }

            $allFolders = [];
            $foldersByUser = [];
            $allUsers = [];
            $errors = [];
            $totalFolders = 0;

            foreach ($serviceUsers as $serviceUser) {
                try {
                    $userFolders = $this->listContactFoldersForUser($serviceUser->email, $withContactCount);

                    if (isset($userFolders['error'])) {
                        $errors[] = "Error for user {$serviceUser->email}: " . $userFolders['error'];
                        // Still add user info even if there was an error
                        $foldersByUser[] = [
                            'user' => [
                                'id' => $serviceUser->id,
                                'email' => $serviceUser->email,
                                'ms_id' => $serviceUser->ms_id,
                                'name' => $serviceUser->title
                            ],
                            'folders' => [],
                            'folder_count' => 0,
                            'error' => $userFolders['error']
                        ];
                        continue;
                    }

                    $folders = $userFolders['folders'] ?? [];
                    $folderCount = count($folders);
                    $totalFolders += $folderCount;

                    // Add folders to combined list (for backward compatibility)
                    $allFolders = array_merge($allFolders, $folders);

                    // Group folders by user
                    $foldersByUser[] = [
                        'user' => [
                            'id' => $serviceUser->id,
                            'email' => $serviceUser->email,
                            'ms_id' => $serviceUser->ms_id,
                            'name' => $serviceUser->title
                        ],
                        'folders' => $folders,
                        'folder_count' => $folderCount
                    ];

                    // Add user info for summary
                    $allUsers[] = [
                        'id' => $serviceUser->id,
                        'email' => $serviceUser->email,
                        'ms_id' => $serviceUser->ms_id,
                        'name' => $serviceUser->title,
                        'folder_count' => $folderCount
                    ];

                } catch (Exception $e) {
                    $errors[] = "Error for user {$serviceUser->email}: " . $e->getMessage();
                    MicrosoftGraphUtilities::logOperation('error', 'Failed to list folders for user', [
                        'user_email' => $serviceUser->email,
                        'error' => $e->getMessage()
                    ]);

                    // Add user info even if there was an error
                    $foldersByUser[] = [
                        'user' => [
                            'id' => $serviceUser->id,
                            'email' => $serviceUser->email,
                            'ms_id' => $serviceUser->ms_id,
                            'name' => $serviceUser->title
                        ],
                        'folders' => [],
                        'folder_count' => 0,
                        'error' => $e->getMessage()
                    ];
                }
            }

            MicrosoftGraphUtilities::logOperation('info', 'Listed contact folders for all service users', [
                'total_users' => count($serviceUsers),
                'successful_users' => count($allUsers),
                'total_folders' => $totalFolders,
                'errors' => count($errors),
                'with_contact_count' => $withContactCount
            ]);

            return [
                'folders' => $allFolders, // All folders combined (backward compatibility)
                'folders_by_user' => $foldersByUser, // Folders grouped by user
                'service_users' => $allUsers,
                'total_folders' => $totalFolders,
                'total_users' => count($allUsers),
                'errors' => $errors
            ];

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', 'Failed to list contact folders for all users', [
                'error' => $e->getMessage()
            ]);
            return [
                'folders' => [],
                'folders_by_user' => [],
                'service_users' => [],
                'total_folders' => 0,
                'total_users' => 0,
                'errors' => [$e->getMessage()]
            ];
        }
    }    /**
     * List contact folders for a specific user
     */
    protected function listContactFoldersForUser(string $userEmail, bool $withContactCount = false): array
    {
        try {
            $endpoint = "users/{$userEmail}/contactFolders";
            $folders = $this->apiClient->getAllPaginatedItems($endpoint);

            // Enrich folders with contact count information if requested
            $enrichedFolders = [];
            foreach ($folders as $folder) {
                $enrichedFolder = is_object($folder) ? json_decode(json_encode($folder), true) : $folder;

                // Get contact count for this folder only if requested
                if ($withContactCount) {
                    try {
                        $contactCount = $this->getContactCountForFolder($userEmail, $enrichedFolder['id'] ?? '');
                        $enrichedFolder['contactCount'] = $contactCount;
                    } catch (Exception $e) {
                        // If we can't get the contact count, default to 0
                        MicrosoftGraphUtilities::logOperation('warning', 'Could not get contact count for folder', [
                            'user_email' => $userEmail,
                            'folder_id' => $enrichedFolder['id'] ?? 'unknown',
                            'folder_name' => $enrichedFolder['displayName'] ?? 'unknown',
                            'error' => $e->getMessage()
                        ]);
                        $enrichedFolder['contactCount'] = 0;
                    }
                } else {
                    // Don't query for contact count if not requested
                    $enrichedFolder['contactCount'] = 0;
                }

                $enrichedFolders[] = $enrichedFolder;
            }

            // Get user information if available locally
            $serviceUser = MicrosoftUser::where('email', $userEmail)->first();

            return [
                'folders' => $enrichedFolders,
                'service_user' => $serviceUser ? [
                    'id' => $serviceUser->id,
                    'email' => $serviceUser->email,
                    'ms_id' => $serviceUser->ms_id,
                    'name' => $serviceUser->title
                ] : null,
                'user_email' => $userEmail
            ];

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', 'Failed to list contact folders for user', [
                'user_email' => $userEmail,
                'error' => $e->getMessage()
            ]);
            return [
                'folders' => [],
                'service_user' => null,
                'user_email' => $userEmail,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Get the contact count for a specific folder
     */
    protected function getContactCountForFolder(string $userEmail, string $folderId): int
    {
        try {
            // Get contacts with minimal data for counting (just id field for efficiency)
            $endpoint = "users/{$userEmail}/contactFolders/{$folderId}/contacts?\$select=id&\$top=999";

            MicrosoftGraphUtilities::logOperation('debug', 'Fetching contacts for counting', [
                'user_email' => $userEmail,
                'folder_id' => substr($folderId, 0, 20) . '...',
                'endpoint' => $endpoint
            ]);

            $contacts = $this->apiClient->getAllPaginatedItems($endpoint);
            $contactCount = count($contacts);

            // If no contacts found, let's try a different approach to verify
            if ($contactCount === 0) {
                // Try getting contacts without $select to see if there's an issue with the query
                MicrosoftGraphUtilities::logOperation('debug', 'No contacts found with $select, trying without $select', [
                    'user_email' => $userEmail,
                    'folder_id' => substr($folderId, 0, 20) . '...'
                ]);

                try {
                    $alternativeEndpoint = "users/{$userEmail}/contactFolders/{$folderId}/contacts?\$top=10";
                    $alternativeContacts = $this->apiClient->getAllPaginatedItems($alternativeEndpoint);
                    $alternativeCount = count($alternativeContacts);

                    MicrosoftGraphUtilities::logOperation('debug', 'Alternative contact query result', [
                        'user_email' => $userEmail,
                        'folder_id' => substr($folderId, 0, 20) . '...',
                        'alternative_count' => $alternativeCount,
                        'first_contact_sample' => !empty($alternativeContacts) ? (is_array($alternativeContacts[0]) ? $alternativeContacts[0] : json_decode(json_encode($alternativeContacts[0]), true)) : null
                    ]);

                    if ($alternativeCount > 0) {
                        // If alternative query returns contacts, there might be an issue with $select
                        MicrosoftGraphUtilities::logOperation('warning', 'Contacts found with alternative query but not with $select query', [
                            'user_email' => $userEmail,
                            'folder_id' => substr($folderId, 0, 20) . '...',
                            'select_count' => $contactCount,
                            'alternative_count' => $alternativeCount
                        ]);
                        return $alternativeCount;
                    }
                } catch (Exception $altException) {
                    MicrosoftGraphUtilities::logOperation('debug', 'Alternative query also failed', [
                        'user_email' => $userEmail,
                        'folder_id' => substr($folderId, 0, 20) . '...',
                        'error' => $altException->getMessage()
                    ]);
                }
            }

            MicrosoftGraphUtilities::logOperation('info', 'Successfully counted contacts in folder', [
                'user_email' => $userEmail,
                'folder_id' => substr($folderId, 0, 20) . '...',
                'contact_count' => $contactCount,
                'folder_name' => $this->getFolderName($userEmail, $folderId)
            ]);

            return $contactCount;

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('warning', 'Failed to get contact count for folder', [
                'user_email' => $userEmail,
                'folder_id' => substr($folderId, 0, 20) . '...',
                'error' => $e->getMessage()
            ]);
            return 0;
        }
    }

    /**
     * Helper method to get folder name for debugging
     */
    protected function getFolderName(string $userEmail, string $folderId): ?string
    {
        try {
            $endpoint = "users/{$userEmail}/contactFolders/{$folderId}?\$select=displayName";
            $folder = $this->apiClient->getWithRetry($endpoint, ['operation' => 'get_folder_name']);
            return is_object($folder) ? ($folder->displayName ?? null) : ($folder['displayName'] ?? null);
        } catch (Exception $e) {
            return null;
        }
    }

    /**
     * Delete a contact folder permanently with retry logic
     */
    public function deleteContactFolder(string $folderId, string $userEmail): bool
    {
        try {
            $this->apiClient->postWithRetry(
                "users/{$userEmail}/contactFolders/{$folderId}/permanentDelete",
                [],
                [
                    'operation' => 'delete_folder',
                    'folder_id' => $folderId,
                    'user_email' => $userEmail
                ]
            );

            MicrosoftGraphUtilities::logOperation('delete', "Permanently deleted contact folder: {$folderId}");
            return true;

        } catch (Exception $e) {
            MicrosoftGraphUtilities::logOperation('error', 'Failed to permanently delete contact folder', [
                'folder_id' => $folderId,
                'user_email' => $userEmail,
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Delete all contact folders for all service users
     */
    public function deleteAllContactFolders(bool $dryRun = false): array
    {
        $this->apiClient->setDryRun($dryRun);

        $stats = [
            'folders_deleted' => 0,
            'users_processed' => 0,
            'errors' => []
        ];

        try {
            // Get all service users
            $serviceUsers = MicrosoftUser::whereNotNull('ms_id')->where('ms_id', '!=', '')->get();
            if ($serviceUsers->isEmpty()) {
                throw new Exception('No service users found');
            }

            MicrosoftGraphUtilities::logOperation('info', "Found " . count($serviceUsers) . " service users to process");

            foreach ($serviceUsers as $serviceUser) {
                try {
                    $stats['users_processed']++;

                    // Get folders for this specific user
                    $response = $this->listContactFoldersForUser($serviceUser->email);

                    if (isset($response['error'])) {
                        $stats['errors'][] = "Error listing folders for {$serviceUser->email}: " . $response['error'];
                        continue;
                    }

                    $folders = $response['folders'] ?? [];
                    MicrosoftGraphUtilities::logOperation('info', "Found " . count($folders) . " folders for user: {$serviceUser->email}");

                    foreach ($folders as $folder) {
                        $folderId = $this->apiClient->getResponseValue($folder, 'id');
                        $displayName = $this->apiClient->getResponseValue($folder, 'displayName');

                        if ($folderId && $this->deleteContactFolder($folderId, $serviceUser->email)) {
                            $stats['folders_deleted']++;
                            MicrosoftGraphUtilities::logOperation('delete', "Deleted folder: {$displayName} for user: {$serviceUser->email}");
                        } else {
                            $stats['errors'][] = "Failed to delete folder: {$displayName} (ID: {$folderId}) for user: {$serviceUser->email}";
                        }
                    }

                } catch (Exception $e) {
                    $stats['errors'][] = "Error processing user {$serviceUser->email}: " . $e->getMessage();
                    MicrosoftGraphUtilities::logOperation('error', 'Failed to delete folders for user', [
                        'user_email' => $serviceUser->email,
                        'error' => $e->getMessage()
                    ]);
                }
            }

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

                // Then delete all folders
                $foldersDeleted = ContactFolder::count();
                ContactFolder::query()->delete();
                MicrosoftGraphUtilities::logOperation('info', "Cleaned up contact_folders table ({$foldersDeleted} records deleted)");
            }

            MicrosoftGraphUtilities::logOperation('success', 'Completed deletion of all contact folders', [
                'users_processed' => $stats['users_processed'],
                'folders_deleted' => $stats['folders_deleted'],
                'errors' => count($stats['errors'])
            ]);

        } catch (Exception $e) {
            $stats['errors'][] = $e->getMessage();
            MicrosoftGraphUtilities::logOperation('error', 'Failed to delete all contact folders', [
                'error' => $e->getMessage()
            ]);
        }

        return $stats;
    }

    /**
     * Get published committees with proper joins
     */
    protected function getPublishedCommittees()
    {
        return Committee::full()
            ->where('status', 'published')
            ->get();
    }

    /**
     * Get published workgroups with proper joins
     */
    protected function getPublishedWorkgroups()
    {
        return WorkGroup::full()
            ->where('status', 'published')
            ->get();
    }

    /**
     * Get the model class name for a model type
     */
    protected function getModelClass(string $modelType): string
    {
        return match($modelType) {
            'committee' => Committee::class,
            'workgroup' => WorkGroup::class,
            default => throw new Exception("Unknown model type: {$modelType}")
        };
    }
}
