using skydiveLogs_api.Domain;
using skydiveLogs_api.DomainBusiness.Interfaces;
using System;
using System.Runtime.Caching;

namespace skydiveLogs_api.DomainBusiness
{
    public class CacheService : ICacheService
    {
        #region Public Constructors

        public CacheService()
        {
            _memoryCache = MemoryCache.Default;
        }

        #endregion Public Constructors

        #region Private Fields

        private readonly MemoryCache _memoryCache;

        #endregion Private Fields

        #region Public Methods

        /// <summary>
        /// Checks if a specific cache entry exists for a given type and user ID.
        /// </summary>
        /// <param name="type">The type of data to check for in the cache.</param>
        /// <param name="userId">Optional user ID to distinguish cache entries. Default is 0.</param>
        /// <returns>True if the cache entry exists and is valid, otherwise false.</returns>
        public bool Contains(CacheType type, int userId = 0)
        {
            var key = GetKey(userId, type);
            return _memoryCache.Contains(key.ToString());
        }

        /// <summary>
        /// Removes a cache entry from the store.
        /// </summary>
        /// <param name="type">The type of data to remove from the cache.</param>
        /// <param name="userId">Optional user ID to identify the cache entry. Default is 0.</param>
        public void Delete(CacheType type, int userId = 0)
        {
            var key = GetKey(userId, type);
            _memoryCache.Remove(key.ToString());
        }

        /// <summary>
        /// Retrieves data from the cache for a specified type and user ID.
        /// </summary>
        /// <typeparam name="T">The expected type of the cached data.</typeparam>
        /// <param name="type">The type of data to retrieve from the cache.</param>
        /// <param name="userId">Optional user ID to identify the cache entry. Default is 0.</param>
        /// <returns>The cached data cast to type T, or default(T) if not found or expired.</returns>
        public T Get<T>(CacheType type, int userId = 0)
        {
            var key = GetKey(userId, type);
            return (T)_memoryCache.Get(key.ToString());
        }

        /// <summary>
        /// Stores data in the cache with a specified expiry time.
        /// </summary>
        /// <param name="type">The unique identifier for the cache key.</param>
        /// <param name="value">The object data to be cached.</param>
        /// <param name="duration">The duration in milliseconds before the cache entry expires.</param>
        /// <param name="userId">Optional user ID to distinguish cache entries. Default is 0.</param>
        /// <exception cref="ArgumentException">Thrown when duration is less than or equal to zero.</exception>
        public void Put(CacheType type, object value, int duration, int userId = 0)
        {
            var key = GetKey(userId, type);
            if (duration <= 0)
                throw new ArgumentException("Duration cannot be less or equal to zero", "duration");

            var policy = new CacheItemPolicy
            {
                AbsoluteExpiration = DateTime.Now.AddMilliseconds(duration)
            };

            _memoryCache.Set(key.ToString(), value, policy);
        }

        /// <summary>
        /// Generates a cache key by combining the user ID and cache type.
        /// </summary>
        /// <param name="userId">The user ID to include in the key.</param>
        /// <param name="type">The cache type to include in the key.</param>
        /// <returns>A concatenated string key combining userId and type.</returns>
        private string GetKey(int userId, CacheType type)
        {
            return $"{userId}-{type}";
        }

        #endregion Public Methods
    }
}
