namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;

/// <summary>
/// Computes the single source-reference prefix an MxAccess Gateway alarm stream can be
/// opened with while still covering every subscribed source. <c>StreamAlarms</c> carries
/// ONE <c>alarm_filter_prefix</c>, so the only pushable union of N source references is
/// their longest common prefix.
///
/// <para>
/// The prefix is a BANDWIDTH optimisation, never correctness: the DataConnectionActor's
/// per-source match plus condition-type gate stays authoritative, mirroring the OPC UA
/// server-side WhereClause stance. A too-broad prefix (including the empty,
/// gateway-wide one) is therefore always safe.
/// </para>
/// </summary>
internal static class AlarmFilterPrefix
{
    /// <summary>
    /// Longest common prefix of the supplied source references, or the empty string when
    /// they share none (or when any source is empty — an empty source means "everything").
    /// </summary>
    /// <param name="sourceReferences">Currently subscribed source references.</param>
    /// <returns>The prefix to open the gateway alarm stream with; empty = gateway-wide.</returns>
    public static string LongestCommonPrefix(IEnumerable<string> sourceReferences)
    {
        string? prefix = null;
        foreach (var source in sourceReferences)
        {
            if (string.IsNullOrEmpty(source))
                return string.Empty;

            if (prefix is null)
            {
                prefix = source;
                continue;
            }

            var max = Math.Min(prefix.Length, source.Length);
            var common = 0;
            while (common < max && prefix[common] == source[common])
                common++;

            if (common == 0)
                return string.Empty;
            prefix = prefix[..common];
        }

        return prefix ?? string.Empty;
    }

    /// <summary>
    /// Whether an existing stream opened with <paramref name="currentPrefix"/> already
    /// covers <paramref name="sourceReference"/>. Only a source falling OUTSIDE the
    /// current prefix justifies restarting the stream; an unsubscribe never does,
    /// because the prefix it leaves behind is at worst too broad — which costs
    /// bandwidth, not correctness.
    /// </summary>
    /// <param name="currentPrefix">Prefix the live stream was opened with.</param>
    /// <param name="sourceReference">Newly subscribed source reference.</param>
    /// <returns><c>true</c> when the live stream already carries this source.</returns>
    public static bool Covers(string currentPrefix, string sourceReference) =>
        currentPrefix.Length == 0 || sourceReference.StartsWith(currentPrefix, StringComparison.Ordinal);
}
