import { Note, SyncQueueItem } from '../types/note';

const DB_NAME = 'notesDB';
const DB_VERSION = 1;

interface NotesDB {
  notes: IDBObjectStore;
  syncQueue: IDBObjectStore;
}

class IndexedDBManager {
  private db: IDBDatabase | null = null;
  private initPromise: Promise<IDBDatabase> | null = null;

  async init(): Promise<IDBDatabase> {
    if (this.db) {
      return this.db;
    }

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

    this.initPromise = new Promise((resolve, reject) => {
      const request = indexedDB.open(DB_NAME, DB_VERSION);

      request.onerror = () => {
        reject(request.error);
      };

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

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

        // Хранилище для заметок
        if (!db.objectStoreNames.contains('notes')) {
          const notesStore = db.createObjectStore('notes', { keyPath: 'id' });
          notesStore.createIndex('user_id', 'user_id', { unique: false });
          notesStore.createIndex('created_at', 'created_at', { unique: false });
          notesStore.createIndex('syncStatus', 'syncStatus', { unique: false });
        }

        // Хранилище для очереди синхронизации
        if (!db.objectStoreNames.contains('syncQueue')) {
          const syncStore = db.createObjectStore('syncQueue', {
            keyPath: 'id',
          });
          syncStore.createIndex('timestamp', 'timestamp', { unique: false });
          syncStore.createIndex('noteId', 'noteId', { unique: false });
        }
      };
    });

    return this.initPromise;
  }

  private async getStore(
    storeName: keyof NotesDB,
    mode: IDBTransactionMode = 'readonly'
  ): Promise<IDBObjectStore> {
    await this.init();
    if (!this.db) {
      throw new Error('Database not initialized');
    }

    const transaction = this.db.transaction([storeName], mode);
    return transaction.objectStore(storeName);
  }

  // ===== CRUD операции для заметок =====

  async getAllNotes(): Promise<Note[]> {
    const store = await this.getStore('notes');
    return new Promise((resolve, reject) => {
      const request = store.getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getNotesByUserId(userId: number): Promise<Note[]> {
    const store = await this.getStore('notes');
    return new Promise((resolve, reject) => {
      const request = store.index('user_id').getAll(userId);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getNote(id: number | string): Promise<Note | undefined> {
    const store = await this.getStore('notes');
    return new Promise((resolve, reject) => {
      const request = store.get(id);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async saveNote(note: Note): Promise<void> {
    const store = await this.getStore('notes', 'readwrite');
    return new Promise((resolve, reject) => {
      const request = store.put(note);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async saveNotes(notes: Note[]): Promise<void> {
    if (notes.length === 0) {
      return;
    }

    await this.init();
    if (!this.db) {
      throw new Error('Database not initialized');
    }

    const transaction = this.db.transaction(['notes'], 'readwrite');
    const store = transaction.objectStore('notes');

    return new Promise((resolve, reject) => {
      let completed = 0;
      for (const note of notes) {
        const request = store.put(note);
        request.onerror = () => reject(request.error);
        request.onsuccess = () => {
          completed++;
          if (completed === notes.length) {
            resolve();
          }
        };
      }
    });
  }

  async deleteNote(id: number | string): Promise<void> {
    const store = await this.getStore('notes', 'readwrite');
    return new Promise((resolve, reject) => {
      const request = store.delete(id);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async clearAllNotes(): Promise<void> {
    const store = await this.getStore('notes', 'readwrite');
    return new Promise((resolve, reject) => {
      const request = store.clear();
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  // ===== Операции с очередью синхронизации =====

  async addToSyncQueue(item: Omit<SyncQueueItem, 'id'>): Promise<string> {
    const store = await this.getStore('syncQueue', 'readwrite');
    const id = `sync-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    const syncItem: SyncQueueItem = {
      ...item,
      id,
    };

    return new Promise((resolve, reject) => {
      const request = store.add(syncItem);
      request.onsuccess = () => resolve(id);
      request.onerror = () => reject(request.error);
    });
  }

  async getSyncQueue(): Promise<SyncQueueItem[]> {
    const store = await this.getStore('syncQueue');
    return new Promise((resolve, reject) => {
      const request = store.index('timestamp').getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getSyncQueueItem(id: string): Promise<SyncQueueItem | undefined> {
    const store = await this.getStore('syncQueue');
    return new Promise((resolve, reject) => {
      const request = store.get(id);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async removeFromSyncQueue(id: string): Promise<void> {
    const store = await this.getStore('syncQueue', 'readwrite');
    return new Promise((resolve, reject) => {
      const request = store.delete(id);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async updateSyncQueueItem(id: string, updates: Partial<SyncQueueItem>): Promise<void> {
    // Сначала получаем элемент
    const item = await this.getSyncQueueItem(id);
    if (!item) {
      throw new Error('Sync queue item not found');
    }

    // Затем открываем новую транзакцию для обновления
    const store = await this.getStore('syncQueue', 'readwrite');
    return new Promise((resolve, reject) => {
      const request = store.put({ ...item, ...updates });
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async clearSyncQueue(): Promise<void> {
    const store = await this.getStore('syncQueue', 'readwrite');
    return new Promise((resolve, reject) => {
      const request = store.clear();
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * Полная очистка всех данных из IndexedDB (заметки и очередь синхронизации)
   */
  async clearAll(): Promise<void> {
    await Promise.all([
      this.clearAllNotes(),
      this.clearSyncQueue(),
    ]);
  }

  // ===== Утилиты =====

  async getPendingSyncCount(): Promise<number> {
    const queue = await this.getSyncQueue();
    return queue.length;
  }

  async isNoteSynced(noteId: number | string): Promise<boolean> {
    const note = await this.getNote(noteId);
    return note?.syncStatus === 'synced';
  }

  // ===== Методы для получения версии данных =====

  /**
   * Получить версию данных из IndexedDB (последний updated_at)
   */
  async getDataVersion(): Promise<{
    last_updated_at: string | null;
    last_created_at: string | null;
    total_notes: number;
  }> {
    const notes = await this.getAllNotes();
    
    // Фильтруем архивные заметки (как на сервере)
    const activeNotes = notes.filter((note) => note.is_archived === 0);
    
    if (activeNotes.length === 0) {
      return {
        last_updated_at: null,
        last_created_at: null,
        total_notes: 0,
      };
    }

    // Находим максимальный updated_at и created_at
    let maxUpdatedAt: string | null = null;
    let maxCreatedAt: string | null = null;

    for (const note of activeNotes) {
      if (note.updated_at && (!maxUpdatedAt || note.updated_at > maxUpdatedAt)) {
        maxUpdatedAt = note.updated_at;
      }
      if (note.created_at && (!maxCreatedAt || note.created_at > maxCreatedAt)) {
        maxCreatedAt = note.created_at;
      }
    }

    return {
      last_updated_at: maxUpdatedAt,
      last_created_at: maxCreatedAt,
      total_notes: activeNotes.length,
    };
  }

  /**
   * Получить версию данных для конкретного пользователя
   */
  async getDataVersionByUserId(userId: number): Promise<{
    last_updated_at: string | null;
    last_created_at: string | null;
    total_notes: number;
  }> {
    const notes = await this.getNotesByUserId(userId);
    
    // Фильтруем архивные заметки (как на сервере)
    const activeNotes = notes.filter((note) => note.is_archived === 0);
    
    if (activeNotes.length === 0) {
      return {
        last_updated_at: null,
        last_created_at: null,
        total_notes: 0,
      };
    }

    let maxUpdatedAt: string | null = null;
    let maxCreatedAt: string | null = null;

    for (const note of activeNotes) {
      if (note.updated_at && (!maxUpdatedAt || note.updated_at > maxUpdatedAt)) {
        maxUpdatedAt = note.updated_at;
      }
      if (note.created_at && (!maxCreatedAt || note.created_at > maxCreatedAt)) {
        maxCreatedAt = note.created_at;
      }
    }

    return {
      last_updated_at: maxUpdatedAt,
      last_created_at: maxCreatedAt,
      total_notes: activeNotes.length,
    };
  }
}

export const dbManager = new IndexedDBManager();

