import type { Factory, GetSetFactory } from './helpers';
import type {
  ClearOptions,
  DeleteManyPojoOptions,
  DeleteOptions,
  DeletePojoOptions,
  GetOptions,
  GetOrSetForeverOptions,
  GetOrSetForeverPojoOptions,
  GetOrSetOptions,
  GetOrSetPojoOptions,
  GetPojoOptions,
  HasOptions,
  HasPojoOptions,
  SetOptions,
  SetPojoOptions,
} from './main';

/**
 * A cache provider is a class that wraps an underlying cache driver
 * to provide additional features.
 */
export interface CacheProvider {
  /**
   * Set a value in the cache
   * Returns true if the value was set, false otherwise
   */
  set(options: SetPojoOptions): Promise<boolean>
  set(key: string, value: any, options?: SetOptions): Promise<boolean>

  /**
   * Set a value in the cache forever
   */
  setForever(options: SetPojoOptions): Promise<boolean>
  setForever(key: string, value: any, options?: SetOptions): Promise<boolean>

  /**
   * Get a value from the cache, fallback to a default value
   * and set options
   */
  get<T = any>(options: GetPojoOptions<T>): Promise<T>
  get<T = any>(key: string, defaultValue?: Factory<T>, options?: GetOptions): Promise<T>
  get<T = any>(key: string): Promise<T | null | undefined>

  /**
   * Get or set a value in the cache
   */
  getOrSet<T>(options: GetOrSetPojoOptions<T>): Promise<T>
  getOrSet<T>(
    key: string,
    factory: GetSetFactory<T>,
    options?: GetSetFactory<T> | GetOrSetOptions,
  ): Promise<T>

  /**
   * Get or set a value in the cache forever
   */
  getOrSetForever<T>(options: GetOrSetForeverPojoOptions<T>): Promise<T>
  getOrSetForever<T>(key: string, cb: GetSetFactory<T>, opts?: GetOrSetForeverOptions): Promise<T>

  /**
   * Check if a key exists in the cache
   */
  has(options: HasPojoOptions): Promise<boolean>
  has(key: string, options?: HasOptions): Promise<boolean>

  /**
   * Check if a key is missing from the cache
   */
  missing(options: HasPojoOptions): Promise<boolean>
  missing(key: string, options?: HasOptions): Promise<boolean>

  /**
   * Get the value of a key and delete it
   *
   * Returns the value if the key exists, undefined otherwise
   */
  pull<T = any>(key: string): Promise<T | undefined | null>

  /**
   * Delete a key from the cache
   * Returns true if the key was deleted, false otherwise
   */
  delete(options: DeletePojoOptions): Promise<boolean>
  delete(key: string, options?: DeleteOptions): Promise<boolean>

  /**
   * Delete multiple keys from the cache
   */
  deleteMany(options: DeleteManyPojoOptions): Promise<boolean>
  deleteMany(keys: string[], options?: DeleteOptions): Promise<boolean>

  /**
   * Remove all items from the cache
   */
  clear(options?: ClearOptions): Promise<void>

  /**
   * Returns a new instance of the driver namespaced
   */
  namespace(namespace: string): CacheProvider

  /**
   * Closes the connection to the cache
   */
  disconnect(): Promise<void>
}
