using Microsoft.Extensions.Options;

namespace ZB.MOM.WW.LocalDb.Replication;

/// <summary>
/// Validates <see cref="ReplicationOptions"/> at startup, accumulating every failure into one result.
/// <see cref="ReplicationOptions.PeerAddress"/> is only required for the initiator role, which the
/// validator cannot distinguish from a passive node, so its format is checked only when present.
/// </summary>
public sealed class ReplicationOptionsValidator : IValidateOptions<ReplicationOptions>
{
    // Also what keeps SyncBackgroundService's unchecked backoff doubling overflow-safe.
    private static readonly TimeSpan ReconnectBackoffCeiling = TimeSpan.FromDays(1);

    public ValidateOptionsResult Validate(string? name, ReplicationOptions options)
    {
        var failures = new List<string>();

        if (!string.IsNullOrEmpty(options.PeerAddress)
            && (!Uri.TryCreate(options.PeerAddress, UriKind.Absolute, out var uri)
                || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)))
            failures.Add(
                $"LocalDb:Replication:PeerAddress must be an absolute http/https URI; got '{options.PeerAddress}'.");

        // ApiKey needs no validation: null/empty legitimately means "no key auth".

        if (options.FlushInterval <= TimeSpan.Zero)
            failures.Add($"LocalDb:Replication:FlushInterval must be greater than zero; got {options.FlushInterval}.");
        if (options.MaxBatchSize <= 0)
            failures.Add($"LocalDb:Replication:MaxBatchSize must be greater than 0; got {options.MaxBatchSize}.");
        if (options.MaxOplogRows <= 0)
            failures.Add($"LocalDb:Replication:MaxOplogRows must be greater than 0; got {options.MaxOplogRows}.");
        if (options.MaxOplogAge <= TimeSpan.Zero)
            failures.Add($"LocalDb:Replication:MaxOplogAge must be greater than zero; got {options.MaxOplogAge}.");
        if (options.TombstoneRetention <= TimeSpan.Zero)
            failures.Add($"LocalDb:Replication:TombstoneRetention must be greater than zero; got {options.TombstoneRetention}.");
        if (options.ReconnectBackoffMax <= TimeSpan.Zero)
            failures.Add($"LocalDb:Replication:ReconnectBackoffMax must be greater than zero; got {options.ReconnectBackoffMax}.");
        else if (options.ReconnectBackoffMax > ReconnectBackoffCeiling)
            failures.Add($"LocalDb:Replication:ReconnectBackoffMax must be at most {ReconnectBackoffCeiling}; got {options.ReconnectBackoffMax}.");
        if (options.MaintenanceInterval <= TimeSpan.Zero)
            failures.Add($"LocalDb:Replication:MaintenanceInterval must be greater than zero; got {options.MaintenanceInterval}.");
        if (options.MaxHlcDriftAhead <= TimeSpan.Zero)
            failures.Add($"LocalDb:Replication:MaxHlcDriftAhead must be greater than zero; got {options.MaxHlcDriftAhead}.");

        return failures.Count > 0
            ? ValidateOptionsResult.Fail(failures)
            : ValidateOptionsResult.Success;
    }
}
