/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

const mkdirp = require('mkdirp');
const path = require('path');
const Promise = require('promise');

const writeFile = require('../writeFile');
const writeSourceMap = require('./write-sourcemap');
const MAGIC_UNBUNDLE_NUMBER = require('./magic-number');
const MAGIC_UNBUNDLE_FILENAME = 'UNBUNDLE'; // must not start with a dot, as that won't go into the apk
const MODULES_DIR = 'js-modules';

/**
 * Saves all JS modules of an app as single files
 * The startup code (prelude, polyfills etc.) are written to the file
 * designated by the `bundleOuput` option.
 * All other modules go into a 'js-modules' folder that in the same parent
 * directory as the startup file.
 */
function saveAsAssets(bundle, options, log) {
  const {
    'bundle-output': bundleOutput,
    'bundle-encoding': encoding,
    dev,
    'sourcemap-output': sourcemapOutput,
  } = options;

  log('start');
  const {startupCode, modules} = bundle.getUnbundle({minify: !dev});
  log('finish');

  log('Writing bundle output to:', bundleOutput);
  const modulesDir = path.join(path.dirname(bundleOutput), MODULES_DIR);
  const writeUnbundle =
    createDir(modulesDir).then( // create the modules directory first
      Promise.all([
        writeModules(modulesDir, modules, encoding),
        writeFile(bundleOutput, startupCode, encoding),
        writeMagicFlagFile(modulesDir),
      ])
    );
  writeUnbundle.then(() => log('Done writing unbundle output'));

  return Promise.all([writeUnbundle, writeSourceMap(sourcemapOutput, '', log)]);
}

function createDir(dirName) {
  return new Promise((resolve, reject) =>
    mkdirp(dirName, error => error ? reject(error) : resolve()));
}

function createDirectoriesForModules(modulesDir, modules) {
  const dirNames =
    modules.map(name => {
      // get all needed directory names
      const dir = path.dirname(name);
      return dir === '.' || dir === '' ? null : path.join(modulesDir, dir);
    })
    .filter(Boolean) // remove empty directories
    .sort()
    .filter((dir, i, dirs) => {
      // remove parent directories and dedupe.
      // After sorting, parent directories are located before child directories
      const next = dirs[i + 1];
      return !next || next !== dir && !next.startsWith(dir + path.sep);
    });

  return dirNames.reduce(
    (promise, dirName) =>
      promise.then(() => createDir(dirName)), Promise.resolve());
}

function writeModuleFile(module, modulesDir, encoding) {
  const {name, code} = module;
  return writeFile(path.join(modulesDir, name + '.js'), code, encoding);
}

function writeModuleFiles(modules, modulesDir, encoding) {
  const writeFiles =
    modules.map(module => writeModuleFile(module, modulesDir, encoding));
  return Promise.all(writeFiles);
}

function writeModules(modulesDir, modules, encoding) {
  return (
    createDirectoriesForModules(modulesDir, modules.map(({name}) => name))
      .then(() => writeModuleFiles(modules, modulesDir, encoding))
  );
}

function writeMagicFlagFile(outputDir) {
  /* global Buffer: true */
  const buffer = Buffer(4);
  buffer.writeUInt32LE(MAGIC_UNBUNDLE_NUMBER);
  return writeFile(path.join(outputDir, MAGIC_UNBUNDLE_FILENAME), buffer);
}

module.exports = saveAsAssets;