namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared;

/// <summary>
///     Rendering helpers for operator-facing text that comes out of the database.
/// </summary>
/// <remarks>
///     <para>
///         <b>Why this exists (#504).</b> Several pages abbreviated a hash / thumbprint with the range
///         operator — <c>@s.SourceHash[..12]…</c> — which throws
///         <see cref="ArgumentOutOfRangeException"/> the moment the value is shorter than the slice.
///         In Blazor that escapes <c>BuildRenderTree</c> unhandled and takes the <b>whole page</b> to an
///         HTTP 500, not just the offending row. Nothing enforces a minimum length at the schema, the
///         entity, or the render layer, so any hand-written / seeded / migrated row is a live page-kill.
///         The docker-dev seed proved it: its placeholder <c>SourceHash</c> values (<c>h1</c>,
///         <c>h-abs-hr200</c>) made <c>/scripts</c> a hard 500 on every stock bring-up.
///     </para>
///     <para>
///         Use <see cref="Abbreviate"/> for <b>every</b> such prefix. It never throws, and it appends the
///         ellipsis only when it actually truncated — a 2-character hash renders as <c>h1</c>, not
///         <c>h1…</c>, so the display never implies more text than exists.
///     </para>
/// </remarks>
public static class DisplayText
{
    /// <summary>Placeholder rendered for a null / blank value. Matches the em-dash convention the
    /// Admin UI already uses for "nothing to show" cells.</summary>
    public const string Empty = "—";

    /// <summary>
    ///     The first <paramref name="maxLength"/> characters of <paramref name="value"/>, followed by an
    ///     ellipsis when (and only when) characters were dropped.
    /// </summary>
    /// <param name="value">The value to abbreviate. May be null, blank, or shorter than
    /// <paramref name="maxLength"/> — none of which throw.</param>
    /// <param name="maxLength">Maximum number of characters to keep. Values below 1 are treated as 1,
    /// so a caller cannot turn a display bug into a crash.</param>
    /// <returns><see cref="Empty"/> for a null/blank value; the value verbatim when it already fits;
    /// otherwise the truncated prefix plus an ellipsis.</returns>
    public static string Abbreviate(string? value, int maxLength)
    {
        if (string.IsNullOrWhiteSpace(value)) return Empty;
        if (maxLength < 1) maxLength = 1;
        return value.Length <= maxLength ? value : string.Concat(value.AsSpan(0, maxLength), "…");
    }
}
