using Microsoft.Graph;
using Microsoft.Graph.Models;
using SharepointToolbox.Core.Models;
using AppGraphClientFactory = SharepointToolbox.Infrastructure.Auth.GraphClientFactory;

namespace SharepointToolbox.Services;

/// <summary>
/// Enumerates all enabled member users from a tenant via Microsoft Graph,
/// using PageIterator for transparent multi-page iteration.
/// Used by Phase 13's User Directory ViewModel.
/// </summary>
public class GraphUserDirectoryService : IGraphUserDirectoryService
{
    private readonly AppGraphClientFactory _graphClientFactory;

    public GraphUserDirectoryService(AppGraphClientFactory graphClientFactory)
    {
        _graphClientFactory = graphClientFactory;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<GraphDirectoryUser>> GetUsersAsync(
        string clientId,
        bool includeGuests = false,
        IProgress<int>? progress = null,
        CancellationToken ct = default)
    {
        var graphClient = await _graphClientFactory.CreateClientAsync(clientId, ct);

        var response = await graphClient.Users.GetAsync(config =>
        {
            config.QueryParameters.Filter = includeGuests
                ? "accountEnabled eq true"
                : "accountEnabled eq true and userType eq 'Member'";
            config.QueryParameters.Select = new[]
            {
                "displayName", "userPrincipalName", "mail", "department", "jobTitle", "userType"
            };
            config.QueryParameters.Top = 999;
            // No ConsistencyLevel header: standard equality filter does not require eventual consistency
        }, ct);

        if (response is null)
            return Array.Empty<GraphDirectoryUser>();

        var results = new List<GraphDirectoryUser>();

        var pageIterator = PageIterator<User, UserCollectionResponse>.CreatePageIterator(
            graphClient,
            response,
            user =>
            {
                // Honour cancellation inside the callback — returning false stops iteration
                if (ct.IsCancellationRequested)
                    return false;

                results.Add(MapUser(user));
                progress?.Report(results.Count);
                return true;
            });

        await pageIterator.IterateAsync(ct);

        return results;
    }

    /// <summary>
    /// Maps a Graph SDK <see cref="User"/> object to a <see cref="GraphDirectoryUser"/> record.
    /// Extracted as an internal static method to allow direct unit-test coverage of mapping logic
    /// without requiring a live Graph endpoint.
    /// </summary>
    internal static GraphDirectoryUser MapUser(User user) =>
        new(
            DisplayName: user.DisplayName ?? user.UserPrincipalName ?? string.Empty,
            UserPrincipalName: user.UserPrincipalName ?? string.Empty,
            Mail: user.Mail,
            Department: user.Department,
            JobTitle: user.JobTitle,
            UserType: user.UserType);
}
