import type { IStorageDriver } from './types';

interface IndexedDBDriverOptions {
  /** 数据库名称 */
  dbName?: string;
  /** 数据库版本 */
  dbVersion?: number;
  /** 对象存储名称 */
  storeName?: string;
}

/**
 * IndexedDB 驱动
 * 采用懒初始化模式，首次操作时自动打开数据库
 */
class IndexedDBDriver implements IStorageDriver {
  private dbName: string;
  private dbPromise: null | Promise<IDBDatabase> = null;
  private dbVersion: number;
  private storeName: string;

  constructor({
    dbName = 'vben-storage',
    dbVersion = 1,
    storeName = 'kv-store',
  }: IndexedDBDriverOptions = {}) {
    this.dbName = dbName;
    this.dbVersion = dbVersion;
    this.storeName = storeName;
  }

  async clear(): Promise<void> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(this.storeName, 'readwrite');
      const store = tx.objectStore(this.storeName);
      store.clear();

      tx.addEventListener('complete', () => resolve());
      tx.addEventListener('error', () => reject(tx.error));
      tx.addEventListener('abort', () =>
        reject(tx.error ?? new Error('Transaction aborted')),
      );
    });
  }

  async getItem<T>(key: string): Promise<null | T> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(this.storeName, 'readonly');
      const store = tx.objectStore(this.storeName);
      const request = store.get(key);

      request.addEventListener('success', () =>
        resolve(request.result ?? null),
      );
      request.addEventListener('error', () => reject(request.error));
    });
  }

  async keys(): Promise<string[]> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(this.storeName, 'readonly');
      const store = tx.objectStore(this.storeName);
      const request = store.getAllKeys();

      request.addEventListener('success', () =>
        resolve(request.result.map(String)),
      );
      request.addEventListener('error', () => reject(request.error));
    });
  }

  async removeItem(key: string): Promise<void> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(this.storeName, 'readwrite');
      const store = tx.objectStore(this.storeName);
      store.delete(key);

      tx.addEventListener('complete', () => resolve());
      tx.addEventListener('error', () => reject(tx.error));
      tx.addEventListener('abort', () =>
        reject(tx.error ?? new Error('Transaction aborted')),
      );
    });
  }

  async setItem(key: string, value: unknown): Promise<void> {
    const db = await this.getDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(this.storeName, 'readwrite');
      const store = tx.objectStore(this.storeName);
      store.put(value, key);

      tx.addEventListener('complete', () => resolve());
      tx.addEventListener('error', () => reject(tx.error));
      tx.addEventListener('abort', () =>
        reject(tx.error ?? new Error('Transaction aborted')),
      );
    });
  }

  /**
   * 懒初始化：首次调用时打开数据库，后续复用同一个 Promise
   */
  private getDB(): Promise<IDBDatabase> {
    if (!this.dbPromise) {
      this.dbPromise = this.openDB().catch((error) => {
        // allow retry on next call
        this.dbPromise = null;
        throw error;
      });
    }
    return this.dbPromise;
  }

  private openDB(): Promise<IDBDatabase> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.dbVersion);

      request.addEventListener('upgradeneeded', () => {
        const db = request.result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          db.createObjectStore(this.storeName);
        }
      });

      request.addEventListener('success', () => resolve(request.result));
      request.addEventListener('error', () => reject(request.error));
    });
  }
}

export { IndexedDBDriver };
export type { IndexedDBDriverOptions };
