namespace ZB.MOM.WW.OtOpcUa.Cluster;

public static class RoleParser
{
    /// <summary>The admin-role name.</summary>
    public const string Admin = "admin";

    /// <summary>The driver-role name. Single source of truth — downstream <c>DriverRole</c>
    /// constants (<c>RedundancyStateActor</c>, <c>ClusterNodeAddressReconcilerActor</c>,
    /// <c>ServiceCollectionExtensions</c>) alias this value rather than duplicating the literal.</summary>
    public const string Driver = "driver";

    /// <summary>The dev-role name.</summary>
    public const string Dev = "dev";

    /// <summary>Prefix identifying a cluster-scoped role, e.g. <c>cluster-SITE-A</c> (per-cluster mesh, Phase 6).</summary>
    public const string ClusterRolePrefix = "cluster-";

    private static readonly HashSet<string> Allowed = new(StringComparer.Ordinal)
    {
        Admin, Driver, Dev,
    };

    /// <summary>Returns true when <paramref name="role"/> is a cluster-scoped role — starts with
    /// <see cref="ClusterRolePrefix"/> and carries a non-empty ClusterId suffix.</summary>
    /// <param name="role">The role name to test.</param>
    public static bool IsClusterRole(string role) =>
        role.StartsWith(ClusterRolePrefix, StringComparison.Ordinal) && role.Length > ClusterRolePrefix.Length;

    /// <summary>Extracts the ClusterId from a cluster-scoped role, e.g. <c>cluster-SITE-A</c> → <c>SITE-A</c>.
    /// Assumes <see cref="IsClusterRole"/> is true for <paramref name="role"/>.</summary>
    /// <param name="role">The cluster-scoped role name.</param>
    public static string ClusterIdFromRole(string role) => role[ClusterRolePrefix.Length..];

    /// <summary>Parses a comma-separated string of role names into a validated array.</summary>
    /// <param name="raw">The raw role string to parse.</param>
    /// <returns>The distinct, lower-cased, validated role names.</returns>
    public static string[] Parse(string? raw)
    {
        if (string.IsNullOrWhiteSpace(raw)) return Array.Empty<string>();

        var roles = raw
            .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
            .Select(r => r.ToLowerInvariant())
            .Distinct()
            .ToArray();

        foreach (var r in roles)
        {
            if (!Allowed.Contains(r) && !IsClusterRole(r))
                throw new ArgumentException(
                    $"Unknown role '{r}'. Allowed: {string.Join(", ", Allowed)}, or '{ClusterRolePrefix}<ClusterId>'.",
                    nameof(raw));
        }

        return roles;
    }
}
