"use strict";

const state = require('./state');
const logger = require('../logger');
const fs = require('../fs');
const util = require('../util');
const {
  join
} = require('path');
const Table = require('cli-table3');
const dayjs = require('dayjs');
const relativeTime = require('dayjs/plugin/relativeTime');
const chalk = require('chalk');
const _ = require('lodash');
const getFolderSize = require('./get-folder-size');
const Bluebird = require('bluebird');
dayjs.extend(relativeTime);

// output colors for the table
const colors = {
  titles: chalk.white,
  dates: chalk.cyan,
  values: chalk.green,
  size: chalk.gray
};
const logCachePath = () => {
  logger.always(state.getCacheDir());
  return undefined;
};
const clear = () => {
  return fs.removeAsync(state.getCacheDir());
};
const prune = () => {
  const cacheDir = state.getCacheDir();
  const checkedInBinaryVersion = util.pkgVersion();
  let deletedBinary = false;
  return fs.readdirAsync(cacheDir).then(versions => {
    return Bluebird.all(versions.map(version => {
      if (version !== checkedInBinaryVersion) {
        deletedBinary = true;
        const versionDir = join(cacheDir, version);
        return fs.removeAsync(versionDir);
      }
    }));
  }).then(() => {
    if (deletedBinary) {
      logger.always(`Deleted all binary caches except for the ${checkedInBinaryVersion} binary cache.`);
    } else {
      logger.always(`No binary caches found to prune.`);
    }
  }).catch({
    code: 'ENOENT'
  }, () => {
    logger.always(`No Cypress cache was found at ${cacheDir}. Nothing to prune.`);
  });
};
const fileSizeInMB = size => {
  return `${(size / 1024 / 1024).toFixed(1)}MB`;
};

/**
 * Collects all cached versions, finds when each was used
 * and prints a table with results to the terminal
 */
const list = showSize => {
  return getCachedVersions(showSize).then(binaries => {
    const head = [colors.titles('version'), colors.titles('last used')];
    if (showSize) {
      head.push(colors.titles('size'));
    }
    const table = new Table({
      head
    });
    binaries.forEach(binary => {
      const versionString = colors.values(binary.version);
      const lastUsed = binary.accessed ? colors.dates(binary.accessed) : 'unknown';
      const row = [versionString, lastUsed];
      if (showSize) {
        const size = colors.size(fileSizeInMB(binary.size));
        row.push(size);
      }
      return table.push(row);
    });
    logger.always(table.toString());
  });
};
const getCachedVersions = showSize => {
  const cacheDir = state.getCacheDir();
  return fs.readdirAsync(cacheDir).filter(util.isSemver).map(version => {
    return {
      version,
      folderPath: join(cacheDir, version)
    };
  }).mapSeries(binary => {
    // last access time on the folder is different from last access time
    // on the Cypress binary
    const binaryDir = state.getBinaryDir(binary.version);
    const executable = state.getPathToExecutable(binaryDir);
    return fs.statAsync(executable).then(stat => {
      const lastAccessedTime = _.get(stat, 'atime');
      if (!lastAccessedTime) {
        // the test runner has never been opened
        // or could be a test simulating missing timestamp
        return binary;
      }
      const accessed = dayjs(lastAccessedTime).fromNow();
      binary.accessed = accessed;
      return binary;
    }, e => {
      // could not find the binary or gets its stats
      return binary;
    });
  }).mapSeries(binary => {
    if (showSize) {
      const binaryDir = state.getBinaryDir(binary.version);
      return getFolderSize(binaryDir).then(size => {
        return {
          ...binary,
          size
        };
      });
    }
    return binary;
  });
};
module.exports = {
  path: logCachePath,
  clear,
  prune,
  list,
  getCachedVersions
};