import { openDatabase } from 'react-native-sqlite-storage'
import type { SQLiteDatabase } from 'react-native-sqlite-storage'
import { makeAutoObservable, runInAction, observable } from 'mobx'
import { debounce } from 'lodash'
import { safe } from '@esdora/kit'
/**
 * 2分钟对应的毫秒数量
 */
const DB_DELAY_MS = 2 * 60 * 1000
const DB_SETTINGS_NAME = 'settings.db'
/**
 * 使用内存 Map 管理应用设置，并持久化到 SQLite 数据库。
 *
 * - 初始化时从数据库加载设置。
 * - 提供获取和设置单个设置的方法。
 * - 自动将更改保存到数据库。
 *
 * @说明
 * 使用 MobX 的 `makeAutoObservable` 进行状态管理，并假定有用于 SQLite 操作的 `db` 对象。
 */
class SettingStore {
  private settings: Map<string, string>
  private isDbClosed: boolean
  private db: SQLiteDatabase

  constructor() {
    makeAutoObservable(this)
    this.settings = observable.map<string, string>()
    this.isDbClosed = false
  }

  /**
   * 初始化数据库并加载设置
   */
  public initDB = safe(async () => {
    this.db = await openDatabase({ name: DB_SETTINGS_NAME })
    await this._loadFromDB()
  })

  /**
   * 关闭数据库
   */
  private _closeDB = debounce(
    safe(async () => {
      const { db } = this
      if (db) {
        await db.close()
        this.isDbClosed = true
      }
    }),
    DB_DELAY_MS
  )

  /**
   * 自动判断重连
   */
  private _ensureDB = safe(async () => {
    const { db, isDbClosed, initDB } = this

    if (!db || isDbClosed) {
      await initDB()
      this.isDbClosed = false
    }
  })

  /**
   * 将设置从 sqlite 中读取
   */
  private _loadFromDB = safe(async () => {
    const { db, _ensureDB, _closeDB } = this
    await _ensureDB()
    db.transaction((tx) => {
      tx.executeSql('CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT);')
      tx.executeSql('SELECT key, value FROM settings', [], (_, result) => {
        const map = new Map<string, string>()
        for (let i = 0; i < result.rows.length; i++) {
          const row = result.rows.item(i)
          map.set(row.key, row.value)
        }
        runInAction(() => {
          this.settings.clear()
          for (const [key, value] of map.entries()) {
            this.settings.set(key, value)
          }
        })
      })
    })
    _closeDB()
  })

  /**
   * 将设置保存到 sqlite
   */
  private _saveToDB = debounce(
    safe(async () => {
      const { db, _ensureDB, _closeDB } = this
      await _ensureDB()
      // 先读取数据库所有设置
      await new Promise<void>((resolve, reject) => {
        db.transaction((tx) => {
          tx.executeSql(
            'SELECT key, value FROM settings',
            [],
            async (_, result) => {
              const dbMap = new Map<string, string>()
              for (let i = 0; i < result.rows.length; i++) {
                const row = result.rows.item(i)
                dbMap.set(row.key, row.value)
              }
              // 记录需要删除的 key（数据库有但内存没有）
              const toDelete: string[] = []
              dbMap.forEach((_, key) => {
                if (!this.settings.has(key)) {
                  toDelete.push(key)
                }
              })
              // 记录需要插入/更新的 key（内存有但数据库没有或值不同）
              const toUpsert: [string, string][] = []
              this.settings.forEach((value, key) => {
                if (!dbMap.has(key) || dbMap.get(key) !== value) {
                  toUpsert.push([key, value])
                }
              })
              // 执行删除和 upsert
              db.transaction((tx2) => {
                toDelete.forEach((key) => {
                  tx2.executeSql('DELETE FROM settings WHERE key = ?;', [key])
                })
                toUpsert.forEach(([key, value]) => {
                  tx2.executeSql('REPLACE INTO settings (key, value) VALUES (?, ?);', [key, value])
                })
              })
              resolve()
            },
            (err) => {
              reject(err)
            }
          )
        })
      })
      _closeDB()
    }),
    DB_DELAY_MS
  )

  /**
   * 根据 key 获取设置的值。
   *
   * @param key - 要获取的设置的 key。
   * @returns 返回指定 key 对应的值，如果不存在则为 `undefined`。
   */
  getSetting = safe((key: string) => {
    const { settings } = this
    return settings.get(key)
  })

  /**
   * 使用指定的 key 和 value 更新设置，并将更改持久化到数据库。
   *
   * @param key - 要更新的设置的 key。
   * @param value - 设置的新值。
   */
  setSetting = safe(async (key: string, value: string) => {
    const { settings, _saveToDB } = this
    settings.set(key, value)
    await _saveToDB()
  })
}

/**
 * 使用 MobX 和 SQLite 持久化管理应用设置的 Store。
 *
 * @说明
 * - 初始化时从 SQLite 数据库加载设置。
 * - 提供获取和设置的方法。
 * - 自动将更改持久化到数据库。
 *
 * @示例
 * ```typescript
 * settingsStore.setSetting('theme', 'dark');
 * const theme = settingsStore.getSetting('theme');
 * ```
 */
export const settingsStore = new SettingStore()
