using adas_core.Domain.Models.GroupedObservations;
using MongoDB.Bson;

namespace adas_core.Application.Services.Interfaces
{
    public interface ICacheService
    {
        //Métodos básicos
        /// <summary>
        /// Stores the specified value associated with the given key.
        /// </summary>
        /// <param name="key">The identifier used to associate the value.</param>
        /// <param name="value">The value to store for the specified key.</param>
        void SetValue(string key, string value);
        /// <summary>
        /// Retrieves the string value associated with the specified key.
        /// </summary>
        /// <param name="key">The key used to look up the value.</param>
        /// <returns>The value associated with the key, or <c>null</c> if the key is not found.</returns>
        string? GetValue(string key);
        /// <summary>
        /// Asynchronously retrieves an object associated with the specified <paramref name="key"/>, returning null if no entry is found.
        /// When <paramref name="updateExpiration"/> is true, the expiration of the retrieved entry is extended upon access.
        /// </summary>
        /// <param name="key">The unique identifier of the object to retrieve.</param>
        /// <param name="updateExpiration">Indicates whether the expiration of the entry should be extended when it is successfully retrieved. Defaults to true.</param>
        /// <returns>A task that represents the asynchronous retrieval operation. The task result contains the object of type <typeparamref name="T"/> associated with the key, or null if no matching entry exists.</returns>
        Task<T?> GetObjectAsync<T>(string key, bool updateExpiration = true);
        /// <summary>
        /// Asynchronously stores an object of type <typeparamref name="T"/> using the specified key, optionally updating its expiration time.
        /// When <paramref name="updateExpiration"/> is true, the entry's expiration is refreshed; otherwise the existing expiration is preserved.
        /// </summary>
        /// <param name="key">The identifier under which the object will be stored.</param>
        /// <param name="obj">The object to be stored.</param>
        /// <param name="updateExpiration">Specifies whether the expiration time of the entry should be refreshed. Defaults to true.</param>
        Task SetObjectAsync<T>(string key, T obj, bool updateExpiration = true);
        /// <summary>
        /// Asynchronously retrieves an object of type <typeparamref name="T"/> associated with the specified <paramref name="key"/>.
        /// Supports an optional <paramref name="ttlOverride"/> to apply a custom time-to-live and an <paramref name="updateExpiration"/> flag
        /// to control whether the entry's expiration is refreshed on access.
        /// </summary>
        /// <param name="key">The unique identifier of the object to retrieve.</param>
        /// <param name="ttlOverride">An optional time-to-live value that overrides the default expiration period; if <see langword="null"/>, the default TTL is used.</param>
        /// <param name="updateExpiration">A value indicating whether the object's expiration should be extended when it is successfully retrieved.</param>
        /// <returns>A <see cref="Task{T}"/> that represents the asynchronous operation, containing the retrieved object of type <typeparamref name="T"/>, or <see langword="null"/> if the object is not found.</returns>
        Task<T?> GetObjectAsync<T>(string key, TimeSpan? ttlOverride, bool updateExpiration);
        /// <summary>
        /// Asynchronously stores the specified object associated with the given key, using an optional time-to-live override and expiration update behavior.
        /// </summary>
        /// <param name="key">The identifier under which the object will be stored.</param>
        /// <param name="obj">The object to store.</param>
        /// <param name="ttlOverride">An optional <see cref="TimeSpan"/> that overrides the default time-to-live for the stored object.</param>
        /// <param name="updateExpiration">A value indicating whether the expiration of the stored object should be updated based on the provided TTL.</param>
        /// <returns>A <see cref="Task"/> that represents the asynchronous storage operation.</returns>
        Task SetObjectAsync<T>(string key, T obj, TimeSpan? ttlOverride, bool updateExpiration);
        /// <summary>
        /// Asynchronously deletes an object identified by the specified key.
        /// </summary>
        /// <param name="key">The unique identifier of the object to delete.</param>
        Task DeleteObjectAsync(string key);
        /// <summary>
        /// Asynchronously deletes entries matching the specified pattern and returns the number of deleted items.
        /// </summary>
        /// <param name="pattern">The pattern used to match the entries to be deleted.</param>
        /// <returns>A task that represents the asynchronous delete operation. The task result contains the total number of entries that were deleted.</returns>
        Task<long> DeleteByPatternAsync(string pattern);
        /// <summary>
        /// Clears the application cache, removing all cached entries.
        /// </summary>
        void CleanCache();

        
        // Métodos para transparencia y gestión de locks
        
        // GetOrSet (string key)
        /// <summary>
        /// Asynchronously retrieves a cached value for the specified key, or loads and stores it using the provided loader function when the key is not present.
        /// </summary>
        /// <param name="key">The cache key used to identify the stored value.</param>
        /// <param name="loader">The asynchronous function invoked to produce the value when no cached entry exists for the specified key.</param>
        /// <param name="ttlOverride">An optional time-to-live duration that overrides the default expiration for the cached entry; when <c>null</c>, the default TTL is used.</param>
        /// <returns>A task that represents the asynchronous operation. The result is the cached or freshly loaded string value, or <c>null</c> when no value is available.</returns>
        Task<string?> GetOrSetValueAsync(string key, Func<Task<string>> loader, TimeSpan? ttlOverride = null);
        /// <summary>
        /// Asynchronously retrieves an object associated with the specified key from the cache, or invokes the factory to create and cache a new one when the key is not found.
        /// </summary>
        /// <param name="key">The cache key used to identify the stored object.</param>
        /// <param name="factory">The asynchronous function executed to produce the object when no cached value exists for the given key.</param>
        /// <param name="ttl">The optional expiration period for the cached entry; if null, the default cache lifetime is applied.</param>
        /// <param name="cancellationToken">The token used to cancel the asynchronous operation.</param>
        /// <returns>A task that resolves to the cached or newly created object of type <typeparamref name="T"/>.</returns>
        Task<T> GetOrSetObjectAsync<T>(
                    string key,
                    Func<Task<T>> factory,
                    TimeSpan? ttl = null,
                    CancellationToken cancellationToken = default);
        
        // GetOrSet especializado para GroupedObservations
        /// <summary>
        /// Asynchronously retrieves a cached object identified by the patient and grouped field, or creates and stores it via the supplied factory when no cached value exists.
        /// </summary>
        /// <param name="groupedField">The grouped field used to categorize and identify the cached object.</param>
        /// <param name="patientId">The identifier of the patient the object is associated with.</param>
        /// <param name="factory">The asynchronous factory delegate invoked to produce the object when it is not found in the cache.</param>
        /// <param name="ttl">The optional time-to-live duration applied to the cached entry.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
        /// <returns>A task containing the cached or newly created object of type <typeparamref name="T"/>.</returns>
        Task<T> GetOrSetObjectAsync<T>(
                    GroupedField groupedField,
                    ObjectId patientId,
                    Func<Task<T>> factory,
                    TimeSpan? ttl = null,
                    CancellationToken cancellationToken = default);

    }
}