export type CacheOptions = {
  prefix?: string
  defaultTtl?: number
}

export type CacheResult<T> = {
  changed: boolean
  data: T
}

export type CacheKeyMetadata = {
  key: string
  ttl: number
  exists: boolean
}

export type CacheValueWithMetadata<T> = {
  value: T | null
  ttl: number
  exists: boolean
}

export type CacheService = {
  /**
   * Caches data and returns whether it has changed
   * @param key The cache key
   * @param data The data to cache
   * @param options Additional options
   * @returns An object containing whether the data has changed and the cached data
   */
  cache<T>(
    key: string,
    data: T,
    options?: {
      ttl?: number
      serialize?: (data: T) => string
      deserialize?: (data: string) => T
    },
  ): Promise<CacheResult<T>>

  /**
   * Retrieve data from cache
   * @param key The cache key
   * @param deserialize Function to deserialize the data (defaults to JSON.parse)
   * @returns The cached data or null if not found
   */
  get<T>(key: string, deserialize?: (data: string) => T): Promise<T | null>

  /**
   * Replaces the entry with the given new data
   * @param key The cache key
   * @param data New data that will replace the old one
   * @param options Additional options
   */
  forceUpdate<T>(key: string, data: T, options?: { ttl?: number }): Promise<T>

  /**
   * Remove data from cache
   * @param key The cache key
   */
  invalidate(key: string): Promise<void>

  /**
   * Remove all cache entries matching a pattern
   * @param pattern The key pattern to match (e.g., "user:*")
   * @returns The number of keys that were deleted
   */
  invalidatePattern(pattern: string): Promise<number>

  /**
   * Get all keys matching a pattern (within this service's namespace)
   * @param pattern The key pattern to match (e.g., "user:*")
   * @returns Array of matching keys (without the service prefix)
   */
  getKeysByPattern(pattern: string): Promise<string[]>

  /**
   * Get the TTL (time to live) for a specific key
   * @param key The cache key
   * @returns TTL in seconds (-1 if no expiry, -2 if key doesn't exist)
   */
  getTtl(key: string): Promise<number>

  /**
   * Get multiple keys with their metadata
   * @param pattern The key pattern to match
   * @returns Array of objects containing key, ttl, and exists status
   */
  getKeysWithMetadata(pattern: string): Promise<CacheKeyMetadata[]>

  /**
   * Remove multiple specific keys atomically
   * @param keys Array of cache keys to remove
   * @returns The number of keys that were deleted
   */
  invalidateMultiple(keys: string[]): Promise<number>

  /**
   * Check if a key exists in the cache
   * @param key The cache key
   * @returns True if the key exists, false otherwise
   */
  exists(key: string): Promise<boolean>

  /**
   * Get cached value along with its metadata
   * @param key The cache key
   * @param deserialize Function to deserialize the data (defaults to JSON.parse)
   * @returns Object containing value, TTL, and existence status
   */
  getWithMetadata<T>(
    key: string,
    deserialize?: (data: string) => T,
  ): Promise<CacheValueWithMetadata<T>>

  /**
   * Create a namespaced cache service
   * @param namespace The namespace for all cache keys
   * @param options Additional options
   */
  namespace(namespace: string, options?: CacheOptions): CacheService
}
