using DataLayer;
using Dinah.Core;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ApplicationServices;
/// One title Libation is waiting on before attempting it again, and why.
public sealed record DeferredDownload(
string Account,
string AudibleProductId,
DownloadFailureKind Kind,
int ConsecutiveFailures,
DateTimeOffset LastFailedAt,
DateTimeOffset RetryAfter,
string? Reason)
{
/// The label used in the log, the CLI summary and the GUI's skipped-titles breakdown.
public string KindLabel => Kind switch
{
DownloadFailureKind.LicenseDenied => "Audible denied a download license",
DownloadFailureKind.AssetUnavailable => "Audible has no downloadable audio yet",
DownloadFailureKind.ServiceInterruption => "A possible Audible service interruption",
_ => "A previous failure"
};
}
///
/// The titles a bulk or automatic download run should leave alone for now, looked up by library book.
///
/// Read once per run: a run that takes hours must not have its own failures start suppressing the titles
/// still ahead of it in the same pass.
///
///
public sealed class DownloadDeferrals
{
/// No title is deferred. Used by targeted and forced runs, which must attempt what was asked.
public static DownloadDeferrals None { get; } = new([]);
private readonly Dictionary<(string Account, string AudibleProductId), DeferredDownload> byBook;
private DownloadDeferrals(IEnumerable deferred)
=> byBook = deferred.ToDictionary(d => (d.Account, d.AudibleProductId));
public static DownloadDeferrals Create(IEnumerable deferred) => new(deferred);
/// Reads the store. Never throws: a failure here must not stop a download run.
public static DownloadDeferrals Load(DateTimeOffset now)
=> Create(DownloadAttemptFailureStore.GetDeferred(now));
public int Count => byBook.Count;
public bool Any => byBook.Count > 0;
public DeferredDownload? Find(LibraryBook libraryBook)
=> libraryBook.Account is { } account
&& byBook.TryGetValue((account, libraryBook.Book.AudibleProductId), out var deferred)
? deferred
: null;
public bool IsDeferred(LibraryBook libraryBook) => Find(libraryBook) is not null;
}