namespace DodoSSH.Client.Domain;

/// <summary>The merged group, and everything that had to be overridden to produce it.</summary>
/// <param name="Merged">The group to store and push.</param>
/// <param name="Conflicts">Empty when the two sides were reconcilable field by field.</param>
public sealed record HostGroupMergeResult(
    HostGroupSecret Merged,
    IReadOnlyList<HostFieldConflict> Conflicts)
{
    /// <summary>Whether anything had to be overridden.</summary>
    public bool HasConflicts => Conflicts.Count > 0;
}

/// <summary>
/// Merges two divergent versions of a group against the version they both started from.
/// </summary>
/// <remarks>
/// <para>
/// One scalar, so this is the simplest merge in the client and the only interesting thing about it is what it
/// does <em>not</em> have to consider. Filing a host into a group does not write to the group, so two people
/// organising the same vault at the same time never collide here — the only way to reach this code is for two
/// people to rename the same group differently, which is a real disagreement and gets a conflict notice.
/// </para>
/// <para>
/// Nothing is redacted. A group name is the one thing a group has, and a notice saying only that "the name
/// differed" would leave the user unable to tell which of their two names survived.
/// </para>
/// </remarks>
public static class HostGroupSecretMerge
{
    /// <summary>Produces the merged group.</summary>
    /// <param name="ancestor">The version both sides branched from.</param>
    /// <param name="local">The pending local version.</param>
    /// <param name="remote">The server's current version.</param>
    public static HostGroupMergeResult Merge(
        HostGroupSecret ancestor,
        HostGroupSecret local,
        HostGroupSecret remote)
    {
        ArgumentNullException.ThrowIfNull(ancestor);
        ArgumentNullException.ThrowIfNull(local);
        ArgumentNullException.ThrowIfNull(remote);

        var conflicts = new List<HostFieldConflict>();

        var merge = ThreeWayMerge.Scalar(
            ancestor.Label, local.Label, remote.Label, StringComparer.Ordinal);

        if (merge.IsConflicted)
        {
            // The local side always loses a scalar clash — see ThreeWayMerge — so the discarded side is
            // fixed here rather than derived from the outcome.
            conflicts.Add(new HostFieldConflict(
                nameof(HostGroupSecret.Label),
                MergeSide.Local,
                merge.Value,
                merge.Discarded,
                DiscardedWasRemoval: false));
        }

        return new HostGroupMergeResult(new HostGroupSecret { Label = merge.Value }, conflicts);
    }
}
