using System.Diagnostics.CodeAnalysis;

namespace DodoSSH.Client.Domain;

/// <summary>
/// A folder hosts can be filed under, decrypted.
/// </summary>
/// <remarks>
/// <para>
/// One field, which makes this the smallest secret in the vault, and the small size is the feature. A group
/// is a heading in a sidebar; everything else somebody might want from it — which hosts are in it, where it
/// sits in a tree, what colour it is — was considered and left out, each for its own reason.
/// </para>
/// <para>
/// <b>No member list.</b> Membership is a <see cref="HostSecret.GroupId"/> on each host, so filing two
/// different hosts into one group on two machines is two writes to two items. Held here it would be two
/// writes to one item, and <see cref="ThreeWayMerge"/> has no set merge — the collision would resolve by one
/// side winning outright and the other host silently leaving the group it was just put in.
/// </para>
/// <para>
/// <b>No parent.</b> Groups are flat. Two clients can each re-parent A under B and B under A while offline,
/// and a scalar merge accepts both: the result is a cycle that no reader can draw and that the server cannot
/// even see, because it is inside the payload. One level of nesting is not worth a state with no repair path.
/// </para>
/// </remarks>
public sealed record HostGroupSecret : IVaultSecret
{
    /// <summary>What the group is called. The only name it has anywhere.</summary>
    public required string Label { get; init; }

    /// <summary>Whether this is storable, and why not if it is not.</summary>
    /// <remarks>
    /// A blank name is refused rather than defaulted. A group is only ever a heading, so a nameless one is
    /// indistinguishable from the ungrouped heading it would sit next to — and a user cannot select what they
    /// cannot tell apart.
    /// </remarks>
    public bool TryValidate([NotNullWhen(false)] out string? reason)
    {
        if (string.IsNullOrWhiteSpace(Label))
        {
            reason = "A group needs a name.";
            return false;
        }

        reason = null;
        return true;
    }
}
