import Dexie from 'dexie'
import type { AsPlainObject } from 'minisearch'
import type { DocumentRef } from './globals'
import { Notice } from 'obsidian'
import type LocatorPlugin from './main'

export class Database extends Dexie {
  public static readonly dbVersion = 10
  searchHistory!: Dexie.Table<{ id?: number; query: string }, number>
  minisearch!: Dexie.Table<
    {
      date: string
      paths: DocumentRef[]
      data: AsPlainObject
    },
    string
  >
  embeds!: Dexie.Table<{ embedded: string; referencedBy: string[] }, string>

  constructor(private plugin: LocatorPlugin) {
    super(Database.getDbName(plugin.app.appId))
    // Database structure
    this.version(Database.dbVersion).stores({
      searchHistory: '++id',
      minisearch: 'date',
      embeds: 'embedded',
    })
  }

  private static getDbName(appId: string) {
    return 'locator/cache/' + appId
  }

  //#endregion Table declarations

  public async getMinisearchCache(): Promise<{
    paths: DocumentRef[]
    data: AsPlainObject
  } | null> {
    try {
      const cachedIndex = (await this.plugin.database.minisearch.toArray())[0]
      return cachedIndex
    } catch (e) {
      new Notice(
        'Locator - Cache missing or invalid. Some freezes may occur while Locator indexes your vault.'
      )
      console.error('Locator - Error while loading Minisearch cache')
      console.error(e)
      return null
    }
  }

  public async writeMinisearchCache(): Promise<void> {
    const minisearchJson = this.plugin.searchEngine.getSerializedMiniSearch()
    const paths = this.plugin.searchEngine.getSerializedIndexedDocuments()
    const database = this.plugin.database
    await database.minisearch.clear()
    await database.minisearch.add({
      date: new Date().toISOString(),
      paths,
      data: minisearchJson,
    })
    console.debug('Locator - Search cache written')
  }

  /**
   * Deletes Locator databases that have an older version than the current one
   */
  public async clearOldDatabases(): Promise<void> {
    const toDelete = (await indexedDB.databases()).filter(
      db =>
        db.name === Database.getDbName(this.plugin.app.appId) &&
        // version multiplied by 10 https://github.com/dexie/Dexie.js/issues/59
        db.version !== Database.dbVersion * 10
    )
    if (toDelete.length) {
      console.debug('Locator - Those IndexedDb databases will be deleted:')
      for (const db of toDelete) {
        if (db.name) {
          indexedDB.deleteDatabase(db.name)
        }
      }
    }
  }

  public async clearCache() {
    await this.minisearch.clear()
    await this.embeds.clear()
    new Notice('Locator - Cache cleared. Please restart Obsidian.')
  }
}
