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

namespace Beam.Downloaders {
    //public delegate T HtmlTransformer<out T>(HtmlDocument doc);
    //public delegate Task<T> AsyncHtmlTransformer<T>(HtmlDocument doc);
    //public delegate Task<T> AsyncBinaryTransformer<T>(byte[] bin);

    public class DownloadContext {
        private bool disposedValue;
        
        public HttpClient Client { get; }
        public HtmlWeb Web { get; }
        public IProgress<IDownloadReport>? DownloadReporter { get; set; }
        public IProgress<IRetryReport>? RetryReporter { get; set; }
        public AsyncDownloadFailurePredicate<ByteDocument>?[]? AsyncFailurePredicates { get;  }
        public TimeSpan TimeOut { get; set; }
        public IEnumerable<string> Links { get; }
        public CancellationToken CancellationToken { get; }
        public DocumentCache Cache { get; private set; } = [];
        public ILogger? DownloadLogger { get; set; }

        public DownloadContext(HtmlWeb web,
                               HttpClient client,
                               IEnumerable<string> links,
                               CancellationToken cancellationToken = default,
                               IProgress<IDownloadReport>? downloadReporter = null,
                               IProgress<IRetryReport>? retryReporter = null,
                               AsyncDownloadFailurePredicate<ByteDocument>?[]? asyncFailurePredicates = null,
                               TimeSpan? timeOut = null,
                               ILogger? downloadLogger = null) {
            ArgumentNullException.ThrowIfNull(web, nameof(web));
            ArgumentNullException.ThrowIfNull(links, nameof(links));

            Web = web;
            Client = client;
            Links = links;
            CancellationToken = cancellationToken;
            DownloadReporter = downloadReporter;
            RetryReporter = retryReporter;
            AsyncFailurePredicates = asyncFailurePredicates;
            TimeOut = timeOut ?? TimeSpan.FromMinutes(1);
            DownloadLogger = downloadLogger;
        }

        protected virtual void Dispose(bool disposing) {
            if (!disposedValue) {
                if (disposing) {
                    // TODO: dispose managed state (managed objects)
                    Cache = null;
                }

                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
                // TODO: set large fields to null
                disposedValue = true;
            }
        }

        // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
        // ~DownloadContext()
        // {
        //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        //     Dispose(disposing: false);
        // }

        public void Dispose() {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }
}
