/**
 * Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
 */
import IUserDataStore from './IUserDataStore';

export default class IndexedDb implements IUserDataStore {
  private readonly _dbName = '__phenix_store__';
  private readonly _storeName = 'user';
  private readonly _version = 1;
  private _db: IDBDatabase | null = null;
  private _initializationPromise: Promise<void> | null = null;

  constructor() {
    // Database will be initialized on first use
  }

  public static browserSupported(): boolean {
    return 'indexedDB' in window;
  }

  public async get(key: string, defaultValue: string): Promise<string> {
    await this.ensureInitialized();

    return new Promise((resolve): void => {
      const transaction = this._db!.transaction(this._storeName, 'readonly');
      const store = transaction.objectStore(this._storeName);
      const request = store.get(key);

      request.onsuccess = (): void => {
        resolve(request.result !== undefined ? request.result : defaultValue);
      };
      request.onerror = (): void => {
        resolve(defaultValue);
      };
    });
  }

  public async set(key: string, value: string): Promise<void> {
    await this.ensureInitialized();

    return new Promise((resolve, reject): void => {
      const transaction = this._db!.transaction(this._storeName, 'readwrite');
      const store = transaction.objectStore(this._storeName);
      const request = store.put(value, key);

      request.onsuccess = (): void => {
        resolve();
      };
      request.onerror = (): void => {
        reject(new Error('Failed to store value in IndexedDB'));
      };
    });
  }

  private async ensureInitialized(): Promise<void> {
    if (this._db) {
      return;
    }

    if (this._initializationPromise) {
      return this._initializationPromise;
    }

    this._initializationPromise = this.initialize();
    return this._initializationPromise;
  }

  private async initialize(): Promise<void> {
    return new Promise((resolve, reject): void => {
      const openRequest = indexedDB.open(this._dbName, this._version);

      openRequest.onupgradeneeded = (event): void => {
        const db = (event.target as IDBOpenDBRequest).result;

        if (!db.objectStoreNames.contains(this._storeName)) {
          db.createObjectStore(this._storeName);
        }
      };

      openRequest.onsuccess = (): void => {
        this._db = openRequest.result;
        resolve();
      };

      openRequest.onerror = (): void => {
        reject(new Error('Failed to open IndexedDB'));
      };
    });
  }
}
