using System.Text.Json;

namespace ZB.MOM.WW.Auth.ApiKeys.Sqlite;

/// <summary>
/// Serializes API-key scope sets to a canonical JSON array. Scopes are sorted with
/// <see cref="StringComparer.Ordinal"/> so that equal sets always produce identical
/// column text, regardless of insertion order.
/// </summary>
public static class ScopeSerializer
{
    /// <summary>Serializes scopes to an ordinal-sorted JSON array.</summary>
    /// <param name="scopes">The scopes to serialize.</param>
    /// <returns>A JSON array string with elements sorted ordinally.</returns>
    public static string Serialize(IReadOnlySet<string> scopes)
    {
        ArgumentNullException.ThrowIfNull(scopes);
        return JsonSerializer.Serialize(scopes.Order(StringComparer.Ordinal));
    }

    /// <summary>Deserializes scopes from a JSON array string.</summary>
    /// <param name="value">The JSON string to deserialize; may be null or empty.</param>
    /// <returns>
    /// An ordinal-compared set of scopes; empty when the input is null/blank. A malformed or
    /// non-array column (operator tampering, a partial write, a format change, or a buggy writer)
    /// fails closed to an EMPTY set rather than throwing, so a single poisoned row degrades to a
    /// zero-scope identity on the auth path instead of an unhandled <see cref="JsonException"/>.
    /// </returns>
    public static IReadOnlySet<string> Deserialize(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return new HashSet<string>(StringComparer.Ordinal);
        }

        string[]? scopes;
        try
        {
            scopes = JsonSerializer.Deserialize<string[]>(value);
        }
        catch (JsonException)
        {
            // Fail closed: a corrupt scopes column yields no scopes rather than an exception on the
            // verification hot path. The verifier's "only exception path is cancellation" contract
            // is preserved, and a key with an unreadable scope set is left with zero authority.
            return new HashSet<string>(StringComparer.Ordinal);
        }

        return new HashSet<string>(scopes ?? [], StringComparer.Ordinal);
    }
}
