const Command = require("../structures/Command")
const { CommandWords } = require("../constants")
const fs = require("fs")
const { join } = require("path")
const { getAllDbs } = require("../core/db")
const tar = require("tar")
const { progressBar } = require("../utils")
const {
  TEMP_DIR,
  COUCH_DIR,
  MINIO_DIR,
  getConfig,
  replication,
  getPouches,
} = require("./utils")
const { exportObjects, importObjects } = require("./objectStore")

async function exportBackup(opts) {
  const envFile = opts.env || undefined
  let filename = opts["export"] || opts
  if (typeof filename !== "string") {
    filename = `backup-${new Date().toISOString()}.tar.gz`
  }
  const config = await getConfig(envFile)
  const dbList = await getAllDbs(config["COUCH_DB_URL"])
  const { Remote, Local } = getPouches(config)
  if (fs.existsSync(TEMP_DIR)) {
    fs.rmSync(TEMP_DIR, { recursive: true })
  }
  const couchDir = join(TEMP_DIR, COUCH_DIR)
  fs.mkdirSync(TEMP_DIR)
  fs.mkdirSync(couchDir)
  console.log("CouchDB Export")
  const bar = progressBar(dbList.length)
  let count = 0
  for (let db of dbList) {
    bar.update(++count)
    const remote = new Remote(db)
    const local = new Local(join(TEMP_DIR, COUCH_DIR, db))
    await replication(remote, local)
  }
  bar.stop()
  console.log("S3 Export")
  await exportObjects()
  tar.create(
    {
      sync: true,
      gzip: true,
      file: filename,
      cwd: join(TEMP_DIR),
    },
    [COUCH_DIR, MINIO_DIR]
  )
  fs.rmSync(TEMP_DIR, { recursive: true })
  console.log(`Generated export file - ${filename}`)
}

async function importBackup(opts) {
  const envFile = opts.env || undefined
  const filename = opts["import"] || opts
  const config = await getConfig(envFile)
  if (!filename || !fs.existsSync(filename)) {
    console.error("Cannot import without specifying a valid file to import")
    process.exit(-1)
  }
  if (fs.existsSync(TEMP_DIR)) {
    fs.rmSync(TEMP_DIR, { recursive: true })
  }
  fs.mkdirSync(TEMP_DIR)
  tar.extract({
    sync: true,
    cwd: join(TEMP_DIR),
    file: filename,
  })
  const { Remote, Local } = getPouches(config)
  const dbList = fs.readdirSync(join(TEMP_DIR, COUCH_DIR))
  console.log("CouchDB Import")
  const bar = progressBar(dbList.length)
  let count = 0
  for (let db of dbList) {
    bar.update(++count)
    const remote = new Remote(db)
    const local = new Local(join(TEMP_DIR, COUCH_DIR, db))
    await replication(local, remote)
  }
  bar.stop()
  console.log("MinIO Import")
  await importObjects()
  console.log("Import complete")
  fs.rmSync(TEMP_DIR, { recursive: true })
}

async function pickOne(opts) {
  if (opts["import"]) {
    return importBackup(opts)
  } else if (opts["export"]) {
    return exportBackup(opts)
  }
}

const command = new Command(`${CommandWords.BACKUPS}`)
  .addHelp(
    "Allows building backups of Budibase, as well as importing a backup to a new instance."
  )
  .addSubOption(
    "--export [filename]",
    "Export a backup from an existing Budibase installation.",
    exportBackup
  )
  .addSubOption(
    "--import [filename]",
    "Import a backup to a new Budibase installation.",
    importBackup
  )
  .addSubOption(
    "--env [envFile]",
    "Provide an environment variable file to configure the CLI.",
    pickOne
  )

exports.command = command
