using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Online.SharePoint.TenantAdministration;
using Microsoft.SharePoint.Client;
using SharepointToolbox.Core.Models;

namespace SharepointToolbox.Services;

public class SiteListService : ISiteListService
{
    private readonly SessionManager _sessionManager;

    public SiteListService(SessionManager sessionManager)
    {
        _sessionManager = sessionManager;
    }

    public async Task<IReadOnlyList<SiteInfo>> GetSitesAsync(
        TenantProfile profile,
        IProgress<OperationProgress> progress,
        CancellationToken ct)
    {
        ct.ThrowIfCancellationRequested();
        progress.Report(OperationProgress.Indeterminate("Loading sites..."));

        // Obtain the already-authenticated context for the tenant URL, then clone it to
        // the admin URL. Cloning reuses the existing token — no second interactive login.
        ClientContext ctx;
        try
        {
            ctx = await _sessionManager.GetOrCreateContextAsync(profile, ct);
        }
        catch (ServerException ex) when (ex.Message.Contains("Access denied", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException(
                "Site listing requires SharePoint administrator permissions. Connect with an admin account.", ex);
        }

        var adminUrl = DeriveAdminUrl(profile.TenantUrl);
        using var adminCtx = ctx.Clone(adminUrl);

        var results = new List<SiteInfo>();
        var tenant = new Tenant(adminCtx);
        SPOSitePropertiesEnumerable? batch = null;

        // Paginate through all site collections using GetSitePropertiesFromSharePointByFilters.
        // StartIndex = null on the first call; subsequent calls use NextStartIndexFromSharePoint.
        while (batch == null || batch.NextStartIndexFromSharePoint != null)
        {
            ct.ThrowIfCancellationRequested();

            var filter = new SPOSitePropertiesEnumerableFilter
            {
                IncludePersonalSite = PersonalSiteFilter.UseServerDefault,
                StartIndex = batch?.NextStartIndexFromSharePoint,
                IncludeDetail = true
            };

            batch = tenant.GetSitePropertiesFromSharePointByFilters(filter);
            adminCtx.Load(batch);
            await adminCtx.ExecuteQueryAsync();

            foreach (var s in batch)
            {
                if (s.Status == "Active"
                    && !s.Url.Contains("-my.sharepoint.com", StringComparison.OrdinalIgnoreCase))
                {
                    results.Add(new SiteInfo(s.Url, s.Title)
                    {
                        StorageUsedMb = s.StorageUsage,
                        StorageQuotaMb = s.StorageMaximumLevel,
                        Template = s.Template ?? string.Empty
                    });
                }
            }
        }

        return results.OrderBy(s => s.Url).ToList();
    }

    internal static string DeriveAdminUrl(string tenantUrl)
        => Regex.Replace(tenantUrl.TrimEnd('/'),
            @"(https://[^.]+)(\.sharepoint\.com)",
            "$1-admin$2",
            RegexOptions.IgnoreCase);
}
