using System.Text.RegularExpressions;

namespace SharepointToolbox.Core.Helpers;

/// <summary>
/// Classifies a SharePoint group name into one of the known system-group shapes.
/// </summary>
public enum SystemGroupKind
{
    /// <summary>Not a system group — a normal SharePoint group, user, or external user.</summary>
    None,
    /// <summary>Bare "Limited Access System Group" pseudo-principal (no embedded target).</summary>
    LimitedAccessBare,
    /// <summary>"Limited Access System Group For Web {webId}".</summary>
    LimitedAccessWeb,
    /// <summary>"Limited Access System Group For List {listId}".</summary>
    LimitedAccessList,
    /// <summary>"SharingLinks.{itemUniqueId}.{linkType}.{shareId}".</summary>
    SharingLink
}

/// <summary>
/// Result of classifying a SharePoint group name.
/// Carries the embedded GUIDs / link type so a resolver can look up the target.
/// </summary>
public readonly record struct SystemGroupClassification(
    SystemGroupKind Kind,
    Guid?  WebId,
    Guid?  ListId,
    Guid?  ItemUniqueId,
    string? LinkType,
    Guid?  ShareId);

/// <summary>
/// Pure static helpers for classifying SharePoint permission entries.
/// </summary>
public static class PermissionEntryHelper
{
    // "Limited Access System Group For Web {guid}"
    private static readonly Regex LimitedAccessWebRegex = new(
        @"^Limited Access System Group For Web\s+(?<id>[0-9a-fA-F-]{36})\s*$",
        RegexOptions.Compiled | RegexOptions.IgnoreCase);

    // "Limited Access System Group For List {guid}"
    private static readonly Regex LimitedAccessListRegex = new(
        @"^Limited Access System Group For List\s+(?<id>[0-9a-fA-F-]{36})\s*$",
        RegexOptions.Compiled | RegexOptions.IgnoreCase);

    // "SharingLinks.{itemUniqueId}.{linkType}.{shareId}"
    private static readonly Regex SharingLinkRegex = new(
        @"^SharingLinks\.(?<item>[0-9a-fA-F-]{36})\.(?<type>[^.]+)\.(?<share>[0-9a-fA-F-]{36})\s*$",
        RegexOptions.Compiled | RegexOptions.IgnoreCase);

    /// <summary>
    /// Returns true when the login name is a B2B guest (contains #EXT#).
    /// </summary>
    public static bool IsExternalUser(string loginName) =>
        loginName.Contains("#EXT#", StringComparison.OrdinalIgnoreCase);

    /// <summary>
    /// Removes "Limited Access" from the supplied permission levels.
    /// Returns the remaining levels; returns an empty list when all are removed.
    /// </summary>
    public static IReadOnlyList<string> FilterPermissionLevels(IEnumerable<string> levels) =>
        levels
            .Where(l => !string.Equals(l, "Limited Access", StringComparison.OrdinalIgnoreCase))
            .ToList();

    /// <summary>
    /// Returns true when the supplied name/login is the bare "Limited Access System Group"
    /// pseudo-principal — the noise entry with no embedded GUID. The
    /// per-Web/List and SharingLinks variants are NOT filtered: they are enriched.
    /// </summary>
    public static bool IsBareLimitedAccessSystemGroup(string name) =>
        name.Equals("Limited Access System Group", StringComparison.OrdinalIgnoreCase);

    /// <summary>
    /// Classifies a SharePoint group title (the value used in Granted Through). When the
    /// title matches a known system pattern, the embedded GUIDs / link type are parsed
    /// out so a resolver can look up the actual targeted Web/List/Item.
    /// </summary>
    public static SystemGroupClassification Classify(string groupTitle)
    {
        if (string.IsNullOrWhiteSpace(groupTitle))
            return new SystemGroupClassification(SystemGroupKind.None, null, null, null, null, null);

        var trimmed = groupTitle.Trim();

        if (IsBareLimitedAccessSystemGroup(trimmed))
            return new SystemGroupClassification(SystemGroupKind.LimitedAccessBare, null, null, null, null, null);

        var mWeb = LimitedAccessWebRegex.Match(trimmed);
        if (mWeb.Success && Guid.TryParse(mWeb.Groups["id"].Value, out var webId))
            return new SystemGroupClassification(SystemGroupKind.LimitedAccessWeb, webId, null, null, null, null);

        var mList = LimitedAccessListRegex.Match(trimmed);
        if (mList.Success && Guid.TryParse(mList.Groups["id"].Value, out var listId))
            return new SystemGroupClassification(SystemGroupKind.LimitedAccessList, null, listId, null, null, null);

        var mShare = SharingLinkRegex.Match(trimmed);
        if (mShare.Success
            && Guid.TryParse(mShare.Groups["item"].Value, out var itemId)
            && Guid.TryParse(mShare.Groups["share"].Value, out var shareId))
        {
            return new SystemGroupClassification(
                SystemGroupKind.SharingLink, null, null, itemId, mShare.Groups["type"].Value, shareId);
        }

        return new SystemGroupClassification(SystemGroupKind.None, null, null, null, null, null);
    }
}
