using Beam.Models;

namespace Beam.Downloaders;

public sealed class UnitDownloaderOptionsBuilder<OutType> {
    private DownloadTarget _target = DownloadTarget.URL;
    private HttpClient _client = new HttpClient();
    private FailurePredicateOptionsBuilder<ByteDocument> _failureOptionsBuilder = new();
    private FailurePredicateOptions<ByteDocument>? _failurePredicateOptionsOverride = null;
    private SkipPredicateOptionsBuilder<OutType> _skipPredicateOptionsBuilder = new();
    private SkipPredicateOptions<OutType>? _skipPredicateOptionsOverride = null;
    private FragmentOptions? _fragmentOptions;
    private AsyncTransformer<ByteDocument, OutType>? _asyncTransformer;
    private string? _downloadFolder = null;
    private int _bufferSize = 80 * 1024;
    private bool _followRedirects = true;

    public UnitDownloaderOptionsBuilder<OutType> WithTarget(DownloadTarget target) {
        _target = target;
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithFollowRedirects(bool followRedirects) {
        _followRedirects = followRedirects;
        return this;
    }
        
    public UnitDownloaderOptionsBuilder<OutType> WithClient(HttpClient client)
    {
        _client = client ?? throw new System.ArgumentNullException(nameof(client));
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithFailurePredicateOptions(FailurePredicateOptions<ByteDocument>? options)
    {
        _failurePredicateOptionsOverride = options;
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithFailurePredicates(System.Action<FailurePredicateOptionsBuilder<ByteDocument>> configure)
    {
        if (configure == null) throw new System.ArgumentNullException(nameof(configure));
        configure(_failureOptionsBuilder);
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithFragmentOptions(FragmentOptions? options)
    {
        _fragmentOptions = options;
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithSkipPredicates(Action<SkipPredicateOptionsBuilder<OutType>> configure) {
        if (configure == null) throw new ArgumentNullException(nameof(configure));
        configure(_skipPredicateOptionsBuilder);
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithSkipPredicateOptions(
        SkipPredicateOptions<OutType> skipPredicateOptions) {
        _skipPredicateOptionsOverride = skipPredicateOptions;
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithFragments(System.Action<FragmentOptionsBuilder> configure)
    {
        if (configure == null) throw new System.ArgumentNullException(nameof(configure));
        var b = new FragmentOptionsBuilder();
        configure(b);
        _fragmentOptions = b.Build();
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithAsyncTransformer(AsyncTransformer<ByteDocument, OutType> transformer)
    {
        _asyncTransformer = transformer ?? throw new System.ArgumentNullException(nameof(transformer));
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithDownloadFolder(string? downloadFolder)
    {
        _downloadFolder = downloadFolder;
        return this;
    }

    public UnitDownloaderOptionsBuilder<OutType> WithBufferSize(int bytes)
    {
        if (bytes <= 0) throw new System.ArgumentOutOfRangeException(nameof(bytes));
        _bufferSize = bytes;
        return this;
    }

    public UnitDownloaderOptions<OutType> Build()
    {
        if (_asyncTransformer == null)
            throw new System.InvalidOperationException("AsyncTransformer must be provided.");

        _failurePredicateOptionsOverride ??= _failureOptionsBuilder.Build();
        _skipPredicateOptionsOverride ??= _skipPredicateOptionsBuilder.Build();
        
        return new UnitDownloaderOptions<OutType>
        {
            Target = _target,
            Client = _client,
            FailurePredicateOptions = _failurePredicateOptionsOverride,
            SkipPredicateOptions = _skipPredicateOptionsOverride,
            FollowRedirects = _followRedirects,
            FragmentOptions = _fragmentOptions,
            AsyncTransformer = _asyncTransformer,
            DownloadFolder = _downloadFolder,
            BufferSize = _bufferSize
        };
    }
}