import fs from 'node:fs/promises'
import path from 'node:path'

import type {KvStore} from './Types'
import type {Upload} from '../models'

/**
 * 文件键值存储（FileKvStore）
 * 
 * @description 基于文件系统的键值对存储实现，专门用于存储上传文件的元数据
 * @remarks 
 * - 将上传文件的JSON元数据存储在磁盘上，与上传文件同目录
 * - 使用队列机制确保并发安全，每次仅处理一个操作
 * 
 * @typeparam T 存储的数据类型，默认为Upload类型
 */
export class FileKvStore<T = Upload> implements KvStore<T> {
  /** 存储目录路径 */
  directory: string
  /**
   * 构造函数
   * 
   * @param path 指定存储元数据的目录路径
   */
  constructor(path: string) {
    this.directory = path
  }
  /**
   * 根据键获取存储的值
   * 
   * @param key 键名
   * @returns 返回对应的值，如果不存在则返回undefined
   */
  async get(key: string): Promise<T | undefined> {
    try {
      // 读取对应键的JSON文件
      const buffer = await fs.readFile(this.resolve(key), 'utf8')
      // 解析JSON并返回
      return JSON.parse(buffer as string)
    } catch {
      // 文件不存在或读取失败时返回undefined
      return undefined
    }
  }
  /**
   * 存储键值对
   * @param key 键名
   * @param value 要存储的值
   */
  async set(key: string, value: T): Promise<void> {
    // 将值转换为JSON并写入文件
    await fs.writeFile(this.resolve(key), JSON.stringify(value))
  }
  /**
   * 删除指定键的值
   * 
   * @param key 要删除的键名
   */
  async delete(key: string): Promise<void> {
    // 删除对应的JSON文件
    await fs.rm(this.resolve(key))
  }

  /**
   * 列出所有存储的键
   * 
   * @returns 返回已存储的键名数组
   */
  async list(): Promise<Array<string>> {
    // 读取目录中的所有文件
    const files = await fs.readdir(this.directory)
    // 对文件名进行排序
    const sorted = files.sort((a, b) => a.localeCompare(b))
    // 提取文件名（不包含扩展名）
    const name = (file: string) => path.basename(file, '.json')
    // 过滤出有效的tus文件ID
    // 仅保留成对出现的文件（文件名相同，一个有.json扩展名）
    return sorted.filter(
      (file, idx) => idx < sorted.length - 1 && name(file) === name(sorted[idx + 1])
    )
  }

  /**
   * 将键转换为完整的文件路径
   * 
   * @param key 键名
   * @returns 返回完整的文件路径
   * @private
   */
  private resolve(key: string): string {
    // 将键名转换为完整的JSON文件路径
    return path.resolve(this.directory, `${key}.json`)
  }
}