// IndexedDB wrapper for offline Bible storage
export interface BibleVersion {
  id: string
  name: string
  abbreviation: string
  language: string
  isDefault?: boolean
  downloadedAt?: string
  size?: number
  books?: BibleBook[]
}

export interface BibleBook {
  id: string
  name: string
  abbreviation: string
  orderNum: number
  testament: string
  chaptersCount: number
  chapters?: BibleChapter[]
}

export interface BibleChapter {
  id: string
  bookId: string
  chapterNum: number
  verseCount: number
  verses: BibleVerse[]
}

export interface BibleVerse {
  id: string
  chapterId: string
  verseNum: number
  text: string
}

export interface DownloadProgress {
  versionId: string
  status: 'pending' | 'downloading' | 'completed' | 'failed'
  progress: number
  totalBooks: number
  downloadedBooks: number
  totalChapters: number
  downloadedChapters: number
  startedAt: string
  completedAt?: string
  error?: string
}

class OfflineStorage {
  private db: IDBDatabase | null = null
  private readonly dbName = 'BibleStorage'
  private readonly dbVersion = 1

  async init(): Promise<void> {
    if (this.db) return

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

      request.onerror = () => reject(request.error)
      request.onsuccess = () => {
        this.db = request.result
        resolve()
      }

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

        // Bible versions store
        if (!db.objectStoreNames.contains('versions')) {
          const versionsStore = db.createObjectStore('versions', { keyPath: 'id' })
          versionsStore.createIndex('language', 'language', { unique: false })
          versionsStore.createIndex('abbreviation', 'abbreviation', { unique: false })
        }

        // Bible books store
        if (!db.objectStoreNames.contains('books')) {
          const booksStore = db.createObjectStore('books', { keyPath: 'id' })
          booksStore.createIndex('versionId', 'versionId', { unique: false })
          booksStore.createIndex('orderNum', 'orderNum', { unique: false })
        }

        // Bible chapters store
        if (!db.objectStoreNames.contains('chapters')) {
          const chaptersStore = db.createObjectStore('chapters', { keyPath: 'id' })
          chaptersStore.createIndex('bookId', 'bookId', { unique: false })
          chaptersStore.createIndex('versionBookChapter', ['versionId', 'bookId', 'chapterNum'], { unique: true })
        }

        // Download progress store
        if (!db.objectStoreNames.contains('downloads')) {
          const downloadsStore = db.createObjectStore('downloads', { keyPath: 'versionId' })
          downloadsStore.createIndex('status', 'status', { unique: false })
        }

        // Settings store
        if (!db.objectStoreNames.contains('settings')) {
          const settingsStore = db.createObjectStore('settings', { keyPath: 'key' })
        }
      }
    })
  }

  // Version management
  async saveVersion(version: BibleVersion): Promise<void> {
    await this.init()
    return this.performTransaction('versions', 'readwrite', (store) => {
      const versionsStore = store as IDBObjectStore
      versionsStore.put({
        ...version,
        downloadedAt: new Date().toISOString()
      })
    })
  }

  async getVersion(versionId: string): Promise<BibleVersion | null> {
    await this.init()
    return this.performTransaction('versions', 'readonly', (store) => {
      const versionsStore = store as IDBObjectStore
      return versionsStore.get(versionId)
    })
  }

  async getAllVersions(): Promise<BibleVersion[]> {
    await this.init()
    const versions = await this.performTransaction('versions', 'readonly', (store) => {
      const versionsStore = store as IDBObjectStore
      return versionsStore.getAll()
    })
    console.log('[OfflineStorage] getAllVersions result:', versions)
    return versions
  }

  async deleteVersion(versionId: string): Promise<void> {
    await this.init()
    return this.performTransaction(['versions', 'books', 'chapters', 'downloads'], 'readwrite', (stores) => {
      const storeArray = stores as IDBObjectStore[]
      const [versionsStore, booksStore, chaptersStore, downloadsStore] = storeArray

      // Delete version
      versionsStore.delete(versionId)

      // Delete all books for this version
      const booksIndex = booksStore.index('versionId')
      const booksRequest = booksIndex.getAllKeys(versionId)
      booksRequest.onsuccess = () => {
        booksRequest.result.forEach((bookId: any) => {
          booksStore.delete(bookId)

          // Delete all chapters for each book
          const chaptersIndex = chaptersStore.index('bookId')
          const chaptersRequest = chaptersIndex.getAllKeys(bookId)
          chaptersRequest.onsuccess = () => {
            chaptersRequest.result.forEach((chapterId: any) => {
              chaptersStore.delete(chapterId)
            })
          }
        })
      }

      // Delete download progress
      downloadsStore.delete(versionId)
    })
  }

  // Chapter management
  async saveChapter(versionId: string, bookId: string, chapter: BibleChapter): Promise<void> {
    await this.init()
    return this.performTransaction('chapters', 'readwrite', (store) => {
      const chaptersStore = store as IDBObjectStore
      chaptersStore.put({
        ...chapter,
        versionId,
        bookId
      })
    })
  }

  async getChapter(versionId: string, bookId: string, chapterNum: number): Promise<BibleChapter | null> {
    await this.init()
    return this.performTransaction('chapters', 'readonly', (store) => {
      const chaptersStore = store as IDBObjectStore
      const index = chaptersStore.index('versionBookChapter')
      return index.get([versionId, bookId, chapterNum])
    })
  }

  async getChaptersForBook(bookId: string): Promise<BibleChapter[]> {
    await this.init()
    return this.performTransaction('chapters', 'readonly', (store) => {
      const chaptersStore = store as IDBObjectStore
      const index = chaptersStore.index('bookId')
      return index.getAll(bookId)
    })
  }

  // Book management
  async saveBook(versionId: string, book: BibleBook): Promise<void> {
    await this.init()
    return this.performTransaction('books', 'readwrite', (store) => {
      const booksStore = store as IDBObjectStore
      booksStore.put({
        ...book,
        versionId
      })
    })
  }

  async getBooksForVersion(versionId: string): Promise<BibleBook[]> {
    await this.init()
    const books = await this.performTransaction('books', 'readonly', (store) => {
      const booksStore = store as IDBObjectStore
      const index = booksStore.index('versionId')
      return index.getAll(versionId)
    })
    // Sort books by orderNum to maintain biblical order
    return books.sort((a, b) => (a.orderNum || 0) - (b.orderNum || 0))
  }

  // Download progress management
  async saveDownloadProgress(progress: DownloadProgress): Promise<void> {
    await this.init()
    return this.performTransaction('downloads', 'readwrite', (store) => {
      const downloadsStore = store as IDBObjectStore
      downloadsStore.put(progress)
    })
  }

  async getDownloadProgress(versionId: string): Promise<DownloadProgress | null> {
    await this.init()
    return this.performTransaction('downloads', 'readonly', (store) => {
      const downloadsStore = store as IDBObjectStore
      return downloadsStore.get(versionId)
    })
  }

  async getAllDownloads(): Promise<DownloadProgress[]> {
    await this.init()
    return this.performTransaction('downloads', 'readonly', (store) => {
      const downloadsStore = store as IDBObjectStore
      return downloadsStore.getAll()
    })
  }

  async deleteDownloadProgress(versionId: string): Promise<void> {
    await this.init()
    return this.performTransaction('downloads', 'readwrite', (store) => {
      const downloadsStore = store as IDBObjectStore
      downloadsStore.delete(versionId)
    })
  }

  // Settings management
  async saveSetting(key: string, value: any): Promise<void> {
    await this.init()
    return this.performTransaction('settings', 'readwrite', (store) => {
      const settingsStore = store as IDBObjectStore
      settingsStore.put({ key, value, updatedAt: new Date().toISOString() })
    })
  }

  async getSetting(key: string): Promise<any> {
    await this.init()
    const result = await this.performTransaction('settings', 'readonly', (store) => {
      const settingsStore = store as IDBObjectStore
      return settingsStore.get(key)
    })
    return result?.value
  }

  // Storage info
  async getStorageInfo(): Promise<{ used: number; quota: number; percentage: number }> {
    if ('storage' in navigator && 'estimate' in navigator.storage) {
      const estimate = await navigator.storage.estimate()
      const used = estimate.usage || 0
      const quota = estimate.quota || 0
      const percentage = quota > 0 ? (used / quota) * 100 : 0

      return { used, quota, percentage }
    }

    return { used: 0, quota: 0, percentage: 0 }
  }

  // Check if version is available offline
  async isVersionAvailableOffline(versionId: string): Promise<boolean> {
    const version = await this.getVersion(versionId)
    return !!version
  }

  // Get offline reading statistics
  async getOfflineStats(): Promise<{
    totalVersions: number
    totalBooks: number
    totalChapters: number
    storageUsed: number
    lastSyncDate?: string
  }> {
    const versions = await this.getAllVersions()
    let totalBooks = 0
    let totalChapters = 0

    for (const version of versions) {
      const books = await this.getBooksForVersion(version.id)
      totalBooks += books.length

      for (const book of books) {
        const chapters = await this.getChaptersForBook(book.id)
        totalChapters += chapters.length
      }
    }

    const storageInfo = await this.getStorageInfo()
    const lastSyncDate = await this.getSetting('lastSyncDate')

    return {
      totalVersions: versions.length,
      totalBooks,
      totalChapters,
      storageUsed: storageInfo.used,
      lastSyncDate
    }
  }

  // Helper method for transactions
  private performTransaction<T>(
    storeNames: string | string[],
    mode: IDBTransactionMode,
    operation: (store: IDBObjectStore | IDBObjectStore[]) => IDBRequest<T> | Promise<T> | T
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      if (!this.db) {
        reject(new Error('Database not initialized'))
        return
      }

      const transaction = this.db.transaction(storeNames, mode)
      transaction.onerror = () => reject(transaction.error)

      let stores: IDBObjectStore | IDBObjectStore[]
      if (typeof storeNames === 'string') {
        stores = transaction.objectStore(storeNames)
      } else {
        stores = storeNames.map(name => transaction.objectStore(name))
      }

      try {
        const result = operation(stores)

        if (result instanceof Promise) {
          result.then(resolve).catch(reject)
        } else if (result && typeof result === 'object' && 'onsuccess' in result) {
          const request = result as IDBRequest<T>
          request.onsuccess = () => resolve(request.result)
          request.onerror = () => reject(request.error)
        } else {
          resolve(result as T)
        }
      } catch (error) {
        reject(error)
      }
    })
  }

  // Clear all data
  async clearAll(): Promise<void> {
    await this.init()
    return this.performTransaction(['versions', 'books', 'chapters', 'downloads', 'settings'], 'readwrite', (stores) => {
      const storeArray = stores as IDBObjectStore[]
      storeArray.forEach((store: IDBObjectStore) => store.clear())
    })
  }
}

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

// Bible download manager
export class BibleDownloadManager {
  private downloadQueue: Set<string> = new Set()

  async downloadVersion(versionId: string, onProgress?: (progress: DownloadProgress) => void): Promise<void> {
    if (this.downloadQueue.has(versionId)) {
      throw new Error('Version is already being downloaded')
    }

    this.downloadQueue.add(versionId)

    try {
      // Initialize progress
      const progress: DownloadProgress = {
        versionId,
        status: 'downloading',
        progress: 0,
        totalBooks: 0,
        downloadedBooks: 0,
        totalChapters: 0,
        downloadedChapters: 0,
        startedAt: new Date().toISOString()
      }

      await offlineStorage.saveDownloadProgress(progress)
      onProgress?.(progress)

      // Get version info and books
      console.log(`[Download] Starting download for version: ${versionId}`)

      const versionResponse = await fetch(`/api/bible/books?version=${versionId}`)
      if (!versionResponse.ok) {
        throw new Error('Failed to fetch version books')
      }

      const versionData = await versionResponse.json()
      if (!versionData.success) {
        throw new Error(versionData.error || 'Failed to fetch version books')
      }

      const { version, books } = versionData

      progress.totalBooks = books.length
      await offlineStorage.saveDownloadProgress(progress)
      onProgress?.(progress)

      // Save version info
      console.log('[Download] Saving version info:', version)
      await offlineStorage.saveVersion(version)

      // Calculate total chapters from the chapters array in each book
      progress.totalChapters = books.reduce((sum: number, book: any) => sum + (book.chapters?.length || 0), 0)
      await offlineStorage.saveDownloadProgress(progress)
      onProgress?.(progress)

      // Download each book and its chapters
      for (const book of books) {
        console.log(`[Download] Downloading book: ${book.name}`)

        // Map the book data to match our interface
        const bookData = {
          id: book.id,
          name: book.name,
          abbreviation: book.bookKey,
          orderNum: book.orderNum,
          testament: book.testament,
          chaptersCount: book.chapters?.length || 0
        }

        await offlineStorage.saveBook(versionId, bookData)
        progress.downloadedBooks++

        // Download all chapters for this book
        if (book.chapters) {
          for (const chapterInfo of book.chapters) {
            const chapterResponse = await fetch(`/api/bible/chapter?book=${book.id}&chapter=${chapterInfo.chapterNum}&version=${versionId}`)

            if (chapterResponse.ok) {
              const chapterData = await chapterResponse.json()
              if (chapterData.chapter) {
                // Map the chapter data to match our interface
                const mappedChapter = {
                  id: chapterData.chapter.id,
                  bookId: book.id,
                  chapterNum: chapterData.chapter.chapterNum,
                  verseCount: chapterData.chapter.verses.length,
                  verses: chapterData.chapter.verses
                }
                await offlineStorage.saveChapter(versionId, book.id, mappedChapter)
              }
            }

            progress.downloadedChapters++
            progress.progress = Math.round((progress.downloadedChapters / progress.totalChapters) * 100)

            await offlineStorage.saveDownloadProgress(progress)
            onProgress?.(progress)

            // Small delay to prevent overwhelming the API
            await new Promise(resolve => setTimeout(resolve, 50))
          }
        }
      }

      // Mark as completed
      progress.status = 'completed'
      progress.completedAt = new Date().toISOString()
      progress.progress = 100

      await offlineStorage.saveDownloadProgress(progress)
      onProgress?.(progress)

      console.log(`[Download] Version ${versionId} downloaded successfully`)

    } catch (error) {
      console.error(`[Download] Failed to download version ${versionId}:`, error)

      const progress = await offlineStorage.getDownloadProgress(versionId)
      if (progress) {
        progress.status = 'failed'
        progress.error = error instanceof Error ? error.message : 'Unknown error'
        await offlineStorage.saveDownloadProgress(progress)
        onProgress?.(progress)
      }

      throw error
    } finally {
      this.downloadQueue.delete(versionId)
    }
  }

  async deleteVersion(versionId: string): Promise<void> {
    await offlineStorage.deleteVersion(versionId)
    console.log(`[Download] Version ${versionId} deleted from offline storage`)
  }

  isDownloading(versionId: string): boolean {
    return this.downloadQueue.has(versionId)
  }

  async getDownloadedVersions(): Promise<BibleVersion[]> {
    return offlineStorage.getAllVersions()
  }

  async getStorageInfo(): Promise<{ used: number; quota: number; percentage: number }> {
    return offlineStorage.getStorageInfo()
  }
}

// Export singleton instance
export const bibleDownloadManager = new BibleDownloadManager()