import {GluegunCommand, GluegunFilesystem} from 'gluegun'
import * as process from "process";
import {Toolbox} from "gluegun/build/types/domain/toolbox";

const checkForDocker = async (toolbox: Toolbox) => {
  const {system, print} = toolbox;

  print.divider()
  print.info('Checking if the system has docker...');
  const spinner = print.spin('running docker --version');

  try {
    await system.run('docker --version');
    spinner.succeed('Docker is installed on your system!');
    print.info('Checking if current user can run docker...');
    spinner.start('running docker ps');
    try {
      await system.run('docker ps');
      spinner.succeed('You can run docker!');
    } catch (dockerSudoError) {
      spinner.fail('You are not allowed to run docker, please try again with an allowed user (did you forgot to run the command with sudo?).')
      process.exit(2)
    }
  } catch (dockerVersionError) {
    spinner.fail('Docker is not installed on your system!')
    process.exit(1)
  }
}

const isValidPath = (path: string, filesystem: GluegunFilesystem) => filesystem.exists(path) && path !== '' && path !== '/'

const getAndValidateBackupFolder = async (toolbox: Toolbox): Promise<string> => {
  const {prompt, print, filesystem} = toolbox;

  print.divider();

  let backupPath: string = ''
  do {
    const {pathResponse} = await prompt.ask({
      type: 'input',
      name: 'pathResponse',
      message: 'Please, enter the absolute path to the folder containing the backups'
    })
    backupPath = pathResponse

    if (!isValidPath(backupPath, filesystem)) {
      print.error(`${backupPath} is not a valid path! Please try again.`)
      print.newline()
    }
  } while (!isValidPath(backupPath, filesystem))

  return backupPath;
}

const selectAction = async (toolbox: Toolbox): Promise<string> => {
  const {prompt, print} = toolbox;

  print.divider();

  const {action} = await prompt.ask({
    type: 'select',
    name: 'action',
    message: 'Select an action:',
    choices: ['Backup volume', 'Restore volumes', 'exit'],
  });

  return action;
}

const listAndSelectDockerVolumes = async (toolbox: Toolbox): Promise<string[]> => {
  const {system, prompt, print} = toolbox;

  print.divider();

  const spinner = print.spin('running docker volume ls');

  try {
    const volumes = await system.run('docker volume ls --format "{{.Name}}"');
    spinner.succeed('Volumes listed!');
    const volumeList = volumes.split('\n').filter((volume: string) => volume !== '');
    const {selectedVolumes} = await prompt.ask({
      type: 'multiselect',
      name: 'selectedVolumes',
      message: 'Select the volumes you want to backup:',
      choices: volumeList,
    }) as { selectedVolumes: string[] };

    return selectedVolumes;
  } catch (dockerVolumeError) {
    spinner.fail('Failed to list docker volumes!')
    process.exit(3)
  }
}

const runVolumeBackup = async (toolbox: Toolbox, workingDir: string, volumes: string[]) => {
  const {print, system} = toolbox;

  print.divider();
  print.info('starting backup process');
  print.newline();

  const spinner = print.spin();

  for (const volume of volumes) {
    try {
      spinner.start(`backing up volume ${volume}...`)

      await system.run(`docker run --rm -v ${volume}:/volume -v ${workingDir}:/tmp alpine sh -c "cd /volume && tar -c -p -f /tmp/${volume}.tar ."`);

      spinner.succeed(`volume ${volume} backed up!`)
    } catch (error) {
      spinner.fail(`volume ${volume} backup failed!`)
      print.error(error);
      print.newline();
    }
  }
}

const command: GluegunCommand = {
  name: 'docker-volume-manager',
  run: async (toolbox) => {
    const {print} = toolbox

    print.info('####################################')
    print.info('# Welcome to docker-volume-manager #')
    print.info('####################################')

    // check if docker is present on your system
    await checkForDocker(toolbox);

    // select action
    const choice = await selectAction(toolbox);

    if (choice === 'Backup volume') {
      // select the backup location folder
      // @ts-ignore
      const workingFolder = await getAndValidateBackupFolder(toolbox);

      // list and select the volumes
      const volumes = await listAndSelectDockerVolumes(toolbox);

      // run the backup
      await runVolumeBackup(toolbox, workingFolder, volumes);

      print.success('Your volume have been backed up!')
    } else if (choice === 'Restore volumes') {
      // select the backup location folder

      // list and select the archives to restore

      // run the restore

      print.success('your backup have been restored!');
    } else if (choice === 'exit') {
      process.exit(0);
    }

    // should never happen
    process.exit(999);
  },
}

module.exports = command