namespace EllieBot.Services;

public sealed class ImageCache : IImageCache, IEService
{
    private readonly IBotCache _cache;
    private readonly ImagesConfig _ic;
    private readonly Random _rng;
    private readonly IHttpClientFactory _httpFactory;

    public ImageCache(
        IBotCache cache,
        ImagesConfig ic,
        IHttpClientFactory httpFactory)
    {
        _cache = cache;
        _ic = ic;
        _httpFactory = httpFactory;
        _rng = new EllieRandom();
    }

    private static TypedKey<byte[]> GetImageKey(Uri url)
        => new($"image:{url}");

    public async Task<byte[]?> GetImageDataAsync(Uri url)
        => await _cache.GetOrAddAsync(
            GetImageKey(url),
            async () =>
            {
                if (url.IsFile)
                {
                    return await File.ReadAllBytesAsync(url.LocalPath);
                }

                using var http = _httpFactory.CreateClient();
                var bytes = await http.GetByteArrayAsync(url);
                return bytes;
            },
            expiry: TimeSpan.FromHours(48));

    private async Task<byte[]?> GetRandomImageDataAsync(Uri[] urls)
    {
        if (urls.Length == 0)
            return null;

        var url = urls[_rng.Next(0, urls.Length)];

        var data = await GetImageDataAsync(url);
        return data;
    }

    public Task<byte[]?> GetHeadsImageAsync()
        => GetRandomImageDataAsync(_ic.Data.Coins.Heads);

    public Task<byte[]?> GetTailsImageAsync()
        => GetRandomImageDataAsync(_ic.Data.Coins.Tails);

    public Task<byte[]?> GetCurrencyImageAsync()
        => GetRandomImageDataAsync(_ic.Data.Currency);

    public Task<byte[]?> GetXpBackgroundImageAsync()
        => GetImageDataAsync(_ic.Data.Xp.Bg);

    public Task<byte[]?> GetDiceAsync(int num)
        => GetImageDataAsync(_ic.Data.Dice[num]);

    public Task<byte[]?> GetSlotEmojiAsync(int number)
        => GetImageDataAsync(_ic.Data.Slots.Emojis[number]);

    public Task<byte[]?> GetSlotBgAsync()
        => GetImageDataAsync(_ic.Data.Slots.Bg);
}