/**
 * IndexedDB wrapper for offline storage
 * Provides persistent storage for large objects and file data
 */

interface StoreConfig {
  name: string;
  keyPath: string;
  autoIncrement?: boolean;
}

class OfflineStorage {
  private dbName = 'veza_offline_db';
  private dbVersion = 1;
  private db: IDBDatabase | null = null;

  /**
   * Open database connection
   */
  async open(): Promise<IDBDatabase> {
    if (this.db) {
      return this.db;
    }

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

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

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

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;

        // Create object stores for different data types
        if (!db.objectStoreNames.contains('queued_actions')) {
          const actionStore = db.createObjectStore('queued_actions', {
            keyPath: 'id',
          });
          actionStore.createIndex('timestamp', 'timestamp', { unique: false });
          actionStore.createIndex('type', 'type', { unique: false });
        }

        if (!db.objectStoreNames.contains('uploaded_files')) {
          const fileStore = db.createObjectStore('uploaded_files', {
            keyPath: 'id',
          });
          fileStore.createIndex('filename', 'filename', { unique: false });
          fileStore.createIndex('status', 'status', { unique: false });
        }

        if (!db.objectStoreNames.contains('messages')) {
          const messageStore = db.createObjectStore('messages', {
            keyPath: 'id',
          });
          messageStore.createIndex('conversationId', 'conversationId', { unique: false });
          messageStore.createIndex('timestamp', 'timestamp', { unique: false });
        }
      };
    });
  }

  /**
   * Save data to a store
   */
  async save<T>(storeName: string, data: T): Promise<void> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.add(data);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(new Error(`Failed to save data to ${storeName}`));
    });
  }

  /**
   * Update data in a store
   */
  async update<T>(storeName: string, data: T & { id: string }): Promise<void> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.put(data);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(new Error(`Failed to update data in ${storeName}`));
    });
  }

  /**
   * Get data from a store by ID
   */
  async get<T>(storeName: string, id: string): Promise<T | null> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.get(id);

      request.onsuccess = () => {
        resolve(request.result || null);
      };
      request.onerror = () => reject(new Error(`Failed to get data from ${storeName}`));
    });
  }

  /**
   * Get all data from a store
   */
  async getAll<T>(storeName: string): Promise<T[]> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.getAll();

      request.onsuccess = () => {
        resolve(request.result || []);
      };
      request.onerror = () => reject(new Error(`Failed to get all data from ${storeName}`));
    });
  }

  /**
   * Delete data from a store by ID
   */
  async delete(storeName: string, id: string): Promise<void> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.delete(id);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(new Error(`Failed to delete data from ${storeName}`));
    });
  }

  /**
   * Clear all data from a store
   */
  async clear(storeName: string): Promise<void> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.clear();

      request.onsuccess = () => resolve();
      request.onerror = () => reject(new Error(`Failed to clear ${storeName}`));
    });
  }

  /**
   * Query data by index
   */
  async query<T>(
    storeName: string,
    indexName: string,
    query: IDBKeyRange | string
  ): Promise<T[]> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const index = store.index(indexName);
      const request = index.getAll(query);

      request.onsuccess = () => {
        resolve(request.result || []);
      };
      request.onerror = () => reject(new Error(`Failed to query ${storeName}`));
    });
  }

  /**
   * Get count of items in a store
   */
  async count(storeName: string): Promise<number> {
    const db = await this.open();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.count();

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(new Error(`Failed to count ${storeName}`));
    });
  }
}

// Export singleton instance
export const offlineStorage = new OfflineStorage();

// Export types
export interface QueuedAction {
  id: string;
  type: 'send_message' | 'upload_track' | 'update_profile';
  payload: Record<string, any>;
  timestamp: number;
  retries: number;
  status: 'pending' | 'processing' | 'failed' | 'completed';
}

export interface UploadedFile {
  id: string;
  filename: string;
  file: File;
  status: 'pending' | 'uploading' | 'completed' | 'failed';
  progress: number;
  error?: string;
}

export interface OfflineMessage {
  id: string;
  conversationId: string;
  content: string;
  timestamp: number;
  userId: string;
}

