using Beam.Models;

namespace Beam.Downloaders;

public sealed class FailurePredicateOptionsBuilder<TRaw>
{
    private readonly System.Collections.Generic.List<AsyncDownloadFailurePredicate<TRaw>?> _predicates =
        new System.Collections.Generic.List<AsyncDownloadFailurePredicate<TRaw>?>();
    private bool _processInParallel = false;
    private int? _parallelThreads = null;

    public FailurePredicateOptionsBuilder<TRaw> WithPredicate(AsyncDownloadFailurePredicate<TRaw>? predicate)
    {
        _predicates.Add(predicate);
        return this;
    }

    public FailurePredicateOptionsBuilder<TRaw> WithPredicates(System.Collections.Generic.IEnumerable<AsyncDownloadFailurePredicate<TRaw>?> predicates)
    {
        if (predicates == null) throw new System.ArgumentNullException(nameof(predicates));
        _predicates.AddRange(predicates);
        return this;
    }

    public FailurePredicateOptionsBuilder<TRaw> WithPredicates(params AsyncDownloadFailurePredicate<TRaw>?[] predicates)
    {
        _predicates.Clear();
        if (predicates != null) _predicates.AddRange(predicates);
        return this;
    }

    public FailurePredicateOptionsBuilder<TRaw> WithProcessInParallel(bool value = true)
    {
        _processInParallel = value;
        return this;
    }

    public FailurePredicateOptionsBuilder<TRaw> WithParallelThreads(int? threads)
    {
        if (threads.HasValue && threads.Value <= 0)
            throw new System.ArgumentOutOfRangeException(nameof(threads));
        _parallelThreads = threads;
        return this;
    }

    public FailurePredicateOptions<TRaw> Build()
    {
        var arr = _predicates.Count == 0 ? [] : _predicates.ToArray();
        return new FailurePredicateOptions<TRaw>
        {
            AsyncDownloadFailurePredicates = arr,
            ProcessInParallel = _processInParallel,
            ParallelThreads = _parallelThreads
        };
    }
}