using Beam.Abstractions;
using Beam.Models;
using HtmlAgilityPack;
using Microsoft.Extensions.Logging;

namespace Beam.Downloaders {

    public class DownloadContextBuilder {
        private HtmlWeb _web;
        private HttpClient _client;
        private IProgress<IDownloadReport>? _downloadReporter;
        private IProgress<IRetryReport>? _retryReporter;
        private AsyncDownloadFailurePredicate<ByteDocument>?[] _asyncFailurePredicates = [];
        private TimeSpan _timeOut;
        private IEnumerable<string> _links;
        private CancellationToken _cancellationToken;
        private DocumentCache _cache;
        private ILogger? _downloadLogger;

        public DownloadContextBuilder(HttpClient? client = null, HtmlWeb? web = null) {
            // You can initialize defaults here if needed, e.g.:
            // _timeOut = TimeSpan.FromSeconds(30);
            // _cancellationToken = CancellationToken.None;
            _client = client ?? new();
            _web = web ?? new();
            _links = [];
        }

        public DownloadContextBuilder WithWeb(HtmlWeb web) {
            _web = web;
            return this;
        }

        public DownloadContextBuilder WithClient(HttpClient client) {
            _client = client;
            return this;
        }

        public DownloadContextBuilder WithDownloadReporter(IProgress<IDownloadReport> downloadReporter) {
            _downloadReporter = downloadReporter;
            return this;
        }

        public DownloadContextBuilder WithRetryReporter(IProgress<IRetryReport> retryReporter) {
            _retryReporter = retryReporter;
            return this;
        }

        public DownloadContextBuilder WithAsyncFailurePredicates(params AsyncDownloadFailurePredicate<ByteDocument>[] predicates) {
            _asyncFailurePredicates = predicates;
            return this;
        }

        public DownloadContextBuilder WithTimeOut(TimeSpan timeOut) {
            _timeOut = timeOut;
            return this;
        }

        public DownloadContextBuilder WithLinks(IEnumerable<string> links) {
            _links = links;
            return this;
        }

        public DownloadContextBuilder WithCancellationToken(CancellationToken cancellationToken) {
            _cancellationToken = cancellationToken;
            return this;
        }

        public DownloadContextBuilder WithCache(DocumentCache cache) {
            _cache = cache;
            return this;
        }

        public DownloadContextBuilder WithDownloadLogger(ILogger downloadLogger) {
            _downloadLogger = downloadLogger;
            return this;
        }


        public DownloadContext Build() {
            // Construct the DownloadContext<T> using the collected values.
            var context = new DownloadContext(
                web: _web,
                client: _client,
                links: _links,
                cancellationToken: _cancellationToken,
                downloadReporter: _downloadReporter,
                retryReporter: _retryReporter,
                asyncFailurePredicates: _asyncFailurePredicates,
                timeOut: _timeOut,
                downloadLogger: _downloadLogger
            );

            //// Assign the DocumentCache if it's been set in the builder.
            //// (Even though Cache has a private setter, this code assumes builder
            ////  is in the same assembly or that the setter will be made internal.
            ////  Otherwise, remove or adjust this line.)
            //context.Cache = _cache;

            return context;
        }

        public static DownloadContextBuilder FromContext(DownloadContext existing) {
            if (existing == null) throw new ArgumentNullException(nameof(existing));

            return new DownloadContextBuilder(existing.Client, existing.Web)
                .WithLinks(existing.Links)
                .WithCancellationToken(existing.CancellationToken)
                .WithDownloadReporter(existing.DownloadReporter!)
                .WithRetryReporter(existing.RetryReporter!)
                .WithAsyncFailurePredicates(existing.AsyncFailurePredicates ?? Array.Empty<AsyncDownloadFailurePredicate<ByteDocument>>())
                .WithTimeOut(existing.TimeOut)
                .WithDownloadLogger(existing.DownloadLogger!)
                .WithCache(existing.Cache);
        }
    }

}
