using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure
{

    public static class MemoryCacheExtentions
    {
        private static readonly TimeSpan CacheOlescence = TimeSpan.FromMinutes(5);

        /// <summary>
        /// Создать кеш на основе асинхронного запроса к БД.
        /// Ключ кеша - полное имя типа T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memoryCache"></param>
        /// <param name="query"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static Task<IEnumerable<T>> GetOrCreateBasicAsync<T>(this IMemoryCache memoryCache, IQueryable<T> query, CancellationToken token)
            where T : class
        {
            var getter = async (CancellationToken token) =>
            {
                var entities = await query
                    .AsNoTracking()
                    .ToArrayAsync(token);
                return entities.AsEnumerable();
            };
            return memoryCache.GetOrCreateBasicAsync(getter, token);
        }

        /// <summary>
        /// Создать кеш на основе результата выполнения произвольной асинхронной функции.
        /// Ключ кеша - полное имя типа T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memoryCache"></param>
        /// <param name="getterAsync"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static Task<IEnumerable<T>> GetOrCreateBasicAsync<T>(this IMemoryCache memoryCache, Func<CancellationToken, Task<IEnumerable<T>>> getterAsync, CancellationToken token)
        {
            var key = typeof(T).FullName!;
            var cache = memoryCache.GetOrCreateAsync(key, async (cacheEntry) =>
            {
                cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence;
                cacheEntry.SlidingExpiration = CacheOlescence;
                var entities = await getterAsync(token);
                return entities;
            });
            return cache!;
        }

        /// <summary>
        /// Создать кеш на основе запроса к БД.
        /// Ключ кеша - полное имя типа T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memoryCache"></param>
        /// <param name="query"></param>
        /// <returns></returns>
        public static IEnumerable<T> GetOrCreateBasic<T>(this IMemoryCache memoryCache, IQueryable<T> query)
            where T : class
        {
            var getter = () => query
                .AsNoTracking()
                .ToArray();
            return memoryCache.GetOrCreateBasic(getter);
        }

        /// <summary>
        /// Создать кеш на основе результата выполнения произвольной функции.
        /// Ключ кеша - полное имя типа T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memoryCache"></param>
        /// <param name="getter"></param>
        /// <returns></returns>
        public static IEnumerable<T> GetOrCreateBasic<T>(this IMemoryCache memoryCache, Func<IEnumerable<T>> getter)
        {
            var key = typeof(T).FullName!;
            var cache = memoryCache.GetOrCreate(key, cacheEntry =>
            {
                cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence;
                cacheEntry.SlidingExpiration = CacheOlescence;
                return getter();
            });
            return cache!;
        }

        /// <summary>
        /// Сбросить кеш.
        /// Ключ кеша - полное имя типа T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memoryCache"></param>
        public static void DropBasic<T>(this IMemoryCache memoryCache)
            where T : class
        {
            var key = typeof(T).FullName!;

            memoryCache.Remove(key);
        }
    }
}