/**
 * @ai-summary IndexedDB storage adapter for Auth0 and Zustand persistence
 * @ai-context Replaces localStorage with IndexedDB for mobile browser compatibility
 */

import logger from '../../utils/logger';

interface StorageAdapter {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
  clear(): void;
  key(index: number): string | null;
  readonly length: number;
}

interface Auth0Cache {
  get(key: string): Promise<any>;
  set(key: string, value: any): Promise<void>;
  remove(key: string): Promise<void>;
}

class IndexedDBStorage implements StorageAdapter, Auth0Cache {
  private dbName = 'motovaultpro-storage';
  private dbVersion = 1;
  private storeName = 'keyvalue';
  private db: IDBDatabase | null = null;
  private memoryCache = new Map<string, string>();
  private initPromise: Promise<void>;
  private isReady = false;

  constructor() {
    this.initPromise = this.initialize();
  }

  private async initialize(): Promise<void> {
    try {
      this.db = await this.openDatabase();
      await this.loadCacheFromDB();
      this.isReady = true;
      logger.debug('IndexedDB storage initialized successfully');
    } catch (error) {
      logger.error('IndexedDB initialization failed, using memory only', { error: String(error) });
      this.isReady = false;
    }
  }

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

      request.onerror = () => {
        logger.error(`IndexedDB open failed: ${request.error?.message}`);
        resolve(null as any); // Fallback to memory-only mode
      };

      request.onsuccess = () => {
        resolve(request.result);
      };

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          db.createObjectStore(this.storeName);
        }
      };
    });
  }

  private async loadCacheFromDB(): Promise<void> {
    if (!this.db) return;

    return new Promise((resolve) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const store = transaction.objectStore(this.storeName);
      const request = store.openCursor();
      this.memoryCache.clear();

      request.onsuccess = () => {
        const cursor = request.result;
        if (cursor) {
          const key = cursor.key as string;
          const value = cursor.value;
          if (typeof key === 'string' && typeof value === 'string') {
            this.memoryCache.set(key, value);
          }
          cursor.continue();
        } else {
          logger.debug(`IndexedDB loaded ${this.memoryCache.size} items into cache`);
          resolve();
        }
      };

      request.onerror = () => {
        logger.warn('IndexedDB failed to load cache from DB', { error: String(request.error) });
        resolve();
      };
    });
  }

  private async persistToDB(key: string, value: string | null): Promise<void> {
    if (!this.db) return;

    return new Promise((resolve) => {
      const transaction = this.db!.transaction([this.storeName], 'readwrite');
      const store = transaction.objectStore(this.storeName);

      if (value === null) {
        const request = store.delete(key);
        request.onsuccess = () => resolve();
        request.onerror = () => {
          logger.warn(`IndexedDB failed to delete ${key}`, { error: String(request.error) });
          resolve();
        };
      } else {
        const request = store.put(value, key);
        request.onsuccess = () => resolve();
        request.onerror = () => {
          logger.warn(`IndexedDB failed to persist ${key}`, { error: String(request.error) });
          resolve();
        };
      }
    });
  }

  // Synchronous Storage interface (uses memory cache)
  getItem(key: string): string | null {
    return this.memoryCache.get(key) || null;
  }

  setItem(key: string, value: string): void {
    this.memoryCache.set(key, value);

    // Async persist to IndexedDB (non-blocking)
    if (this.isReady) {
      this.persistToDB(key, value).catch(error => {
        logger.warn(`IndexedDB background persist failed for ${key}`, { error: String(error) });
      });
    }
  }

  removeItem(key: string): void {
    this.memoryCache.delete(key);

    // Async remove from IndexedDB (non-blocking)
    if (this.isReady) {
      this.persistToDB(key, null).catch(error => {
        logger.warn(`IndexedDB background removal failed for ${key}`, { error: String(error) });
      });
    }
  }

  clear(): void {
    this.memoryCache.clear();

    // Async clear IndexedDB (non-blocking)
    if (this.db) {
      const transaction = this.db.transaction([this.storeName], 'readwrite');
      const store = transaction.objectStore(this.storeName);
      store.clear();
    }
  }

  async clearAll(): Promise<void> {
    await this.initPromise;
    if (!this.db) {
      this.memoryCache.clear();
      return;
    }
    const tx = this.db.transaction(this.storeName, 'readwrite');
    tx.objectStore(this.storeName).clear();
    await new Promise<void>((resolve, reject) => {
      tx.oncomplete = () => {
        this.memoryCache.clear();
        resolve();
      };
      tx.onerror = () => {
        this.memoryCache.clear();
        reject(tx.error);
      };
      tx.onabort = () => {
        this.memoryCache.clear();
        reject(new Error('Transaction aborted'));
      };
    });
  }

  key(index: number): string | null {
    const keys = Array.from(this.memoryCache.keys());
    return keys[index] || null;
  }

  get length(): number {
    return this.memoryCache.size;
  }

  // Auth0 Cache interface implementation
  // allKeys() eliminates Auth0 SDK's CacheKeyManifest fallback (auth0-spa-js line 2319)
  allKeys(): string[] {
    return Array.from(this.memoryCache.keys());
  }

  async get(key: string): Promise<any> {
    await this.initPromise;
    const value = this.getItem(key);
    return value ? JSON.parse(value) : undefined;
  }

  async set(key: string, value: any): Promise<void> {
    await this.initPromise;
    const stringValue = JSON.stringify(value);
    this.memoryCache.set(key, stringValue);
    // Fire-and-forget: persist to IndexedDB for page reload survival
    this.persistToDB(key, stringValue).catch(error => {
      logger.warn(`IndexedDB background persist failed for ${key}`, { error: String(error) });
    });
  }

  async remove(key: string): Promise<void> {
    await this.initPromise;
    this.memoryCache.delete(key);
    // Fire-and-forget: remove from IndexedDB
    this.persistToDB(key, null).catch(error => {
      logger.warn(`IndexedDB background removal failed for ${key}`, { error: String(error) });
    });
  }

  // Additional methods for enhanced functionality
  async waitForReady(): Promise<void> {
    return this.initPromise;
  }

  get isInitialized(): boolean {
    return this.isReady;
  }

  // For debugging
  getStats() {
    return {
      cacheSize: this.memoryCache.size,
      isReady: this.isReady,
      hasDB: !!this.db
    };
  }
}

// Create singleton instance
export const indexedDBStorage = new IndexedDBStorage();

// For Auth0 compatibility - ensure storage is ready before use
export const createIndexedDBAdapter = () => {
  return indexedDBStorage;
};