namespace ZB.MOM.WW.Configuration;

/// <summary>
/// Accumulates validation failures for an options object. Passed by
/// <see cref="OptionsValidatorBase{TOptions}"/> into your <c>Validate</c> override; each primitive
/// both checks a value and appends a consistently-formatted message on failure. Use
/// <see cref="RequireThat"/>/<see cref="Add"/> for custom or cross-field rules.
/// </summary>
public sealed class ValidationBuilder
{
    private readonly List<string> _failures = [];

    /// <summary>The accumulated failure messages (empty when validation passed).</summary>
    public IReadOnlyList<string> Failures => _failures;

    /// <summary>True when no failures have been accumulated.</summary>
    public bool IsValid => _failures.Count == 0;

    /// <summary>Records <paramref name="message"/> as a failure when <paramref name="ok"/> is false.</summary>
    public ValidationBuilder RequireThat(bool ok, string message)
    {
        if (!ok) _failures.Add(message);
        return this;
    }

    /// <summary>Unconditionally records <paramref name="message"/> as a failure.</summary>
    public ValidationBuilder Add(string message)
    {
        _failures.Add(message);
        return this;
    }

    /// <summary>Requires a non-null, non-whitespace string.</summary>
    public ValidationBuilder Required(string? value, string field) => AddIf(Checks.Required(value, field));

    /// <summary>Requires a TCP port in 1-65535.</summary>
    public ValidationBuilder Port(int value, string field) => AddIf(Checks.Port(value, field));

    /// <summary>Requires a 'host:port' endpoint with a valid port.</summary>
    public ValidationBuilder HostPort(string? value, string field) => AddIf(Checks.HostPort(value, field));

    /// <summary>Requires a strictly positive duration.</summary>
    public ValidationBuilder PositiveTimeSpan(TimeSpan value, string field) => AddIf(Checks.PositiveTimeSpan(value, field));

    /// <summary>
    /// Requires the value to be one of <paramref name="allowed"/> (case-insensitive). A
    /// <c>null</c> value fails this rule; call <see cref="Required"/> first if the field may be
    /// absent and you want a "required" message instead of a "must be one of" message.
    /// </summary>
    public ValidationBuilder OneOf(string? value, IReadOnlyCollection<string> allowed, string field) => AddIf(Checks.OneOf(value, allowed, field));

    /// <summary>Requires a collection with at least <paramref name="min"/> items.</summary>
    public ValidationBuilder MinCount<T>(IReadOnlyCollection<T>? value, int min, string field) => AddIf(Checks.MinCount(value, min, field));

    private ValidationBuilder AddIf(string? message)
    {
        if (message is not null) _failures.Add(message);
        return this;
    }
}
