using Microsoft.Extensions.Caching.Memory;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
namespace BankSampahApp.Services;
/// 
/// Implementation of cache service dengan enhanced functionality
/// 
public class CacheService : ICacheService
{
    private readonly IMemoryCache _cache;
    private readonly ILogger _logger;
    private readonly ConcurrentDictionary _cacheKeys;
    public CacheService(IMemoryCache cache, ILogger logger)
    {
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _cacheKeys = new ConcurrentDictionary();
    }
    /// 
    public async Task GetOrSetAsync(string key, Func> provider, TimeSpan expiration, CacheItemPriority priority = CacheItemPriority.Normal)
    {
        if (_cache.TryGetValue(key, out T? cachedValue))
        {
            _logger.LogDebug("Cache hit for key: {CacheKey}", key);
            return cachedValue!;
        }
        _logger.LogDebug("Cache miss for key: {CacheKey}, fetching fresh data", key);
        var data = await provider();
        Set(key, data, expiration, priority);
        return data;
    }
    /// 
    public async Task GetOrSetWithSlidingAsync(string key, Func> provider, TimeSpan slidingExpiration, TimeSpan absoluteExpiration, CacheItemPriority priority = CacheItemPriority.Normal)
    {
        if (_cache.TryGetValue(key, out T? cachedValue))
        {
            _logger.LogDebug("Cache hit for key: {CacheKey}", key);
            return cachedValue!;
        }
        _logger.LogDebug("Cache miss for key: {CacheKey}, fetching fresh data", key);
        var data = await provider();
        var cacheOptions = new MemoryCacheEntryOptions
        {
            SlidingExpiration = slidingExpiration,
            AbsoluteExpirationRelativeToNow = absoluteExpiration,
            Priority = priority,
            PostEvictionCallbacks = { new PostEvictionCallbackRegistration
            {
                EvictionCallback = (key, value, reason, state) =>
                {
                    _cacheKeys.TryRemove(key.ToString()!, out _);
                    _logger.LogDebug("Cache entry evicted: {Key}, Reason: {Reason}", key, reason);
                }
            }}
        };
        _cache.Set(key, data, cacheOptions);
        _cacheKeys.TryAdd(key, true);
        _logger.LogDebug("Cached data with sliding expiration for key: {CacheKey}", key);
        return data;
    }
    /// 
    public T? Get(string key)
    {
        if (_cache.TryGetValue(key, out T? value))
        {
            _logger.LogDebug("Retrieved cached value for key: {CacheKey}", key);
            return value;
        }
        _logger.LogDebug("Cache miss for key: {CacheKey}", key);
        return default;
    }
    /// 
    public void Set(string key, T value, TimeSpan expiration, CacheItemPriority priority = CacheItemPriority.Normal)
    {
        var cacheOptions = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = expiration,
            Priority = priority,
            PostEvictionCallbacks = { new PostEvictionCallbackRegistration
            {
                EvictionCallback = (key, value, reason, state) =>
                {
                    _cacheKeys.TryRemove(key.ToString()!, out _);
                    _logger.LogDebug("Cache entry evicted: {Key}, Reason: {Reason}", key, reason);
                }
            }}
        };
        _cache.Set(key, value, cacheOptions);
        _cacheKeys.TryAdd(key, true);
        _logger.LogDebug("Cached value for key: {CacheKey}, Expiration: {Expiration}", key, expiration);
    }
    /// 
    public void Remove(string key)
    {
        _cache.Remove(key);
        _cacheKeys.TryRemove(key, out _);
        _logger.LogDebug("Removed cache entry for key: {CacheKey}", key);
    }
    /// 
    public void RemoveByPattern(string pattern)
    {
        var regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
        var keysToRemove = _cacheKeys.Keys.Where(key => regex.IsMatch(key)).ToList();
        foreach (var key in keysToRemove)
        {
            Remove(key);
        }
        _logger.LogDebug("Removed {Count} cache entries matching pattern: {Pattern}", keysToRemove.Count, pattern);
    }
    /// 
    public bool Exists(string key)
    {
        return _cacheKeys.ContainsKey(key);
    }
}