namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy;

/// <summary>
/// A read-only view of the cluster's driver membership, as the manual-failover control sees it.
/// </summary>
/// <param name="PrimaryAddress">
/// The current driver Primary — the oldest Up <c>driver</c> member, the same node
/// <see cref="RedundancyStateActor.SelectDriverPrimary"/> names — or <see langword="null"/> when
/// there is no Up driver member at all.
/// </param>
/// <param name="DriverAddresses">
/// Every Up <c>driver</c> member, oldest first. A manual failover needs at least two: with one
/// there is nothing to fail over to.
/// </param>
public sealed record ManualFailoverSnapshot(string? PrimaryAddress, IReadOnlyList<string> DriverAddresses)
{
    /// <summary>Whether a failover can be performed — i.e. a peer exists to take over.</summary>
    public bool CanFailOver => DriverAddresses.Count >= 2;
}

/// <summary>
/// Operator-initiated, graceful swap of the driver Primary.
/// </summary>
/// <remarks>
///     <para>
///         The mechanism is a cluster <b>Leave</b> of the current Primary, never a <c>Down</c>. The
///         leaving node hands its singletons over through the normal cluster-leave phases,
///         <c>CoordinatedShutdown</c> runs, <c>ActorSystemTerminationWatchdog</c> exits the process,
///         the service supervisor restarts it, and it rejoins as the youngest member — so the
///         survivor becomes the oldest Up driver member and therefore Primary. ServiceLevel 250
///         moves with it and OPC UA clients re-select.
///     </para>
///     <para>
///         Post-Phase-6 (per-cluster mesh split): the Primary is elected per application
///         <c>Cluster</c>, because each cluster's redundant pair now runs its own independent
///         2-node Akka mesh. This service therefore acts on <b>this pair's own</b> Primary.
///     </para>
/// </remarks>
public interface IManualFailoverService
{
    /// <summary>Reads the current driver membership from live cluster state.</summary>
    ManualFailoverSnapshot GetSnapshot();

    /// <summary>
    /// Gracefully removes the current driver Primary from the cluster so its peer takes over.
    /// </summary>
    /// <param name="actor">The authenticated identity performing the failover; recorded in the audit event.</param>
    /// <returns>
    /// The address of the node asked to leave, or <see langword="null"/> when the failover was
    /// refused because no driver peer exists (in which case nothing was changed and nothing audited).
    /// </returns>
    Task<string?> FailOverDriverPrimaryAsync(string actor);
}
