using Microsoft.Extensions.Options;

namespace ZB.MOM.WW.OtOpcUa.Cluster;

/// <summary>
///     Fails the host at startup on a <see cref="MeshTransportOptions"/> shape that would leave
///     commands silently undelivered.
/// </summary>
/// <remarks>
///     Every fault caught here otherwise surfaces as an <i>absence</i> — a deployment that never
///     arrives, an alarm acknowledgement that does nothing, a node whose ack is dropped without a
///     trace. An absence has no stack trace and no failing node to point at, which makes it the
///     most expensive class of misconfiguration to diagnose in the field. Refusing to start is
///     cheaper for everyone.
/// </remarks>
public sealed class MeshTransportOptionsValidator : IValidateOptions<MeshTransportOptions>
{
    /// <inheritdoc />
    public ValidateOptionsResult Validate(string? name, MeshTransportOptions options)
    {
        ArgumentNullException.ThrowIfNull(options);

        var errors = new List<string>();

        var isDps = string.Equals(options.Mode, MeshTransportOptions.ModeDps, StringComparison.OrdinalIgnoreCase);
        var isClusterClient = string.Equals(
            options.Mode, MeshTransportOptions.ModeClusterClient, StringComparison.OrdinalIgnoreCase);

        if (!isDps && !isClusterClient)
        {
            errors.Add(
                $"{MeshTransportOptions.SectionName}:{nameof(MeshTransportOptions.Mode)} is "
                + $"'{options.Mode}'. Expected '{MeshTransportOptions.ModeDps}' or "
                + $"'{MeshTransportOptions.ModeClusterClient}'.");
        }

        if (options.ContactRefreshSeconds <= 0)
        {
            errors.Add(
                $"{MeshTransportOptions.SectionName}:{nameof(MeshTransportOptions.ContactRefreshSeconds)} "
                + $"must be greater than 0 (was {options.ContactRefreshSeconds}). Central rebuilds its "
                + "ClusterClient contact points on this interval; a non-positive value would leave the "
                + "contact set frozen at whatever it held when the node booted.");
        }

        // Contact points are only load-bearing under ClusterClient mode. Requiring them under the
        // default would make the section mandatory everywhere for a feature that is switched off —
        // including on admin-only nodes, which have no reason to carry them at all.
        if (isClusterClient)
        {
            if (options.CentralContactPoints.Length == 0)
            {
                errors.Add(
                    $"{MeshTransportOptions.SectionName}:{nameof(MeshTransportOptions.CentralContactPoints)} "
                    + "is empty. Under ClusterClient mode a driver node cannot reach central without at "
                    + "least one contact point, and its deployment acks would be dropped silently — the "
                    + "deployment would then time out naming a node that is running perfectly.");
            }

            foreach (var contactPoint in options.CentralContactPoints)
            {
                if (!contactPoint.StartsWith("akka.tcp://", StringComparison.Ordinal))
                {
                    errors.Add(
                        $"Contact point '{contactPoint}' must start with 'akka.tcp://' — it is an Akka "
                        + "remoting address (akka.tcp://<system>@<host>:<port>), not a bare host:port.");
                }
                else if (contactPoint.Contains("/user/", StringComparison.Ordinal)
                         || contactPoint.Contains("/system/", StringComparison.Ordinal))
                {
                    errors.Add(
                        $"Contact point '{contactPoint}' carries an actor-path suffix. Configure the "
                        + "node address only — '/system/receptionist' is appended at construction time, "
                        + "and a doubled suffix produces a path that never resolves.");
                }
            }
        }

        return errors.Count == 0
            ? ValidateOptionsResult.Success
            : ValidateOptionsResult.Fail(string.Join(" ", errors));
    }
}
