import type { RedisClientType } from '@redis/client'
import type { KvStore } from './Types'
import type { Upload } from '../models'

/**
 * Redis 基于键值存储的配置存储类。
 * 该类实现了 KvStore 接口，使用 Redis 作为后端存储，支持数据的增删改查操作。
 * 
 * 使用场景：
 * - 适用于需要持久化存储键值对数据的场景。
 * - 适用于需要高性能、高可用性的数据存储需求。
 *
 * @author Mitja Puzigaća <mitjap@gmail.com>
 */
export class RedisKvStore<T = Upload> implements KvStore<T> {
  /**
   * 构造函数，初始化 RedisKvStore 实例。
   * 
   * @param redis Redis 客户端实例，用于与 Redis 服务器进行交互。
   * @param prefix 键的前缀，用于区分不同的存储空间，默认为空字符串。
   */
  constructor(
    private redis: RedisClientType,
    private prefix = ''
  ) {
    this.redis = redis
    this.prefix = prefix
  }

  /**
   * 根据键获取存储的值。
   * 
   * @param key 要获取的值的键。
   * @returns 返回解析后的值，如果键不存在则返回 undefined。
   */
  async get(key: string): Promise<T | undefined> {
    return this.deserializeValue(await this.redis.get(this.prefix + key))
  }

  /**
   * 设置键值对。
   * 
   * @param key 要设置的键。
   * @param value 要设置的值。
   */
  async set(key: string, value: T): Promise<void> {
    await this.redis.set(this.prefix + key, this.serializeValue(value))
  }

  /**
   * 删除指定的键值对。
   * 
   * @param key 要删除的键。
   */
  async delete(key: string): Promise<void> {
    await this.redis.del(this.prefix + key)
  }

  /**
   * 列出所有键。
   * 
   * @returns 返回所有键的数组。
   */
  async list(): Promise<Array<string>> {
    const keys = new Set<string>()
    let cursor = 0
    do {
      const result = await this.redis.scan(cursor, { MATCH: `${this.prefix}*`, COUNT: 20 })
      cursor = result.cursor
      for (const key of result.keys) keys.add(key)
    } while (cursor !== 0)
    return Array.from(keys)
  }

  /**
   * 序列化值。
   * 
   * @param value 要序列化的值。
   * @returns 返回序列化后的字符串。
   */
  private serializeValue(value: T): string {
    return JSON.stringify(value)
  }

  /**
   * 反序列化值。
   * 
   * @param buffer 要反序列化的字符串。
   * @returns 返回反序列化后的值，如果字符串为空则返回 undefined。
   */
  private deserializeValue(buffer: string | null): T | undefined {
    return buffer ? JSON.parse(buffer) : undefined
  }
}