using Microsoft.Extensions.Caching.Memory;
namespace BankSampahApp.Services;
/// 
/// Base service class yang menyediakan functionality umum untuk semua services
/// Menerapkan prinsip DRY dengan mengekstrak common patterns
/// 
public abstract class BaseService
{
    protected readonly ILogger _logger;
    protected readonly IMemoryCache _cache;
    protected readonly IConfiguration _configuration;
    /// 
    /// Constructor untuk base service
    /// 
    /// Logger instance
    /// Memory cache instance
    /// Configuration instance
    protected BaseService(ILogger logger, IMemoryCache cache, IConfiguration configuration)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
    }
    /// 
    /// Executes a function with comprehensive error handling and logging
    /// 
    /// Return type
    /// Function to execute
    /// Name of the operation for logging
    /// Fallback value if operation fails
    /// Result of operation or fallback value
    protected async Task ExecuteWithErrorHandlingAsync(
        Func> operation,
        string operationName,
        T? fallbackValue = default)
    {
        try
        {
            _logger.LogInformation("Starting operation: {OperationName}", operationName);
            var result = await operation();
            _logger.LogInformation("Successfully completed operation: {OperationName}", operationName);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error in operation: {OperationName}", operationName);
            if (fallbackValue is not null)
            {
                _logger.LogWarning("Returning fallback value for operation: {OperationName}", operationName);
                return fallbackValue;
            }
            throw;
        }
    }
    /// 
    /// Gets or sets cached data with configurable expiration
    /// 
    /// Type of cached data
    /// Cache key
    /// Function to get data if not cached
    /// Cache expiration time
    /// Cache priority
    /// Cached or fresh data
    protected async Task GetOrSetCacheAsync(
        string cacheKey,
        Func> dataProvider,
        TimeSpan expiration,
        CacheItemPriority priority = CacheItemPriority.Normal)
    {
        if (_cache.TryGetValue(cacheKey, out T? cachedValue))
        {
            _logger.LogDebug("Retrieved data from cache with key: {CacheKey}", cacheKey);
            return cachedValue!;
        }
        _logger.LogDebug("Cache miss for key: {CacheKey}, fetching fresh data", cacheKey);
        var data = await dataProvider();
        var cacheOptions = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = expiration,
            Priority = priority
        };
        _cache.Set(cacheKey, data, cacheOptions);
        _logger.LogDebug("Cached data with key: {CacheKey} for {Expiration}", cacheKey, expiration);
        return data;
    }
    /// 
    /// Gets or sets cached data with sliding expiration
    /// 
    /// Type of cached data
    /// Cache key
    /// Function to get data if not cached
    /// Sliding expiration time
    /// Absolute expiration time
    /// Cache priority
    /// Cached or fresh data
    protected async Task GetOrSetCacheWithSlidingAsync(
        string cacheKey,
        Func> dataProvider,
        TimeSpan slidingExpiration,
        TimeSpan absoluteExpiration,
        CacheItemPriority priority = CacheItemPriority.Normal)
    {
        if (_cache.TryGetValue(cacheKey, out T? cachedValue))
        {
            _logger.LogDebug("Retrieved data from cache with key: {CacheKey}", cacheKey);
            return cachedValue!;
        }
        _logger.LogDebug("Cache miss for key: {CacheKey}, fetching fresh data", cacheKey);
        var data = await dataProvider();
        var cacheOptions = new MemoryCacheEntryOptions
        {
            SlidingExpiration = slidingExpiration,
            AbsoluteExpirationRelativeToNow = absoluteExpiration,
            Priority = priority
        };
        _cache.Set(cacheKey, data, cacheOptions);
        _logger.LogDebug("Cached data with key: {CacheKey} with sliding expiration", cacheKey);
        return data;
    }
    /// 
    /// Removes data from cache
    /// 
    /// Cache key to remove
    protected void RemoveFromCache(string cacheKey)
    {
        _cache.Remove(cacheKey);
        _logger.LogDebug("Removed cache entry with key: {CacheKey}", cacheKey);
    }
    /// 
    /// Gets configuration value with fallback
    /// 
    /// Type of configuration value
    /// Configuration key
    /// Default value if key not found
    /// Configuration value or default
    protected T GetConfigurationValue(string key, T defaultValue)
    {
        return _configuration.GetValue(key) ?? defaultValue;
    }
    /// 
    /// Validates arguments for null values
    /// 
    /// Dictionary of argument names and values
    /// Thrown if any argument is null
    protected static void ValidateArguments(Dictionary arguments)
    {
        foreach (var arg in arguments)
        {
            if (arg.Value == null)
            {
                throw new ArgumentNullException(arg.Key);
            }
        }
    }
    /// 
    /// Creates a logging scope with operation context
    /// 
    /// Name of the operation
    /// Additional context data
    /// Disposable logging scope
    protected IDisposable? CreateLogScope(string operationName, Dictionary? additionalContext = null)
    {
        var scopeData = new Dictionary
        {
            ["Operation"] = operationName,
            ["Service"] = GetType().Name
        };
        if (additionalContext != null)
        {
            foreach (var item in additionalContext)
            {
                scopeData[item.Key] = item.Value;
            }
        }
        return _logger.BeginScope(scopeData);
    }
}