"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.mergeFiles = mergeFiles;

var _fs = require("fs");

var _path = _interopRequireDefault(require("path"));

var _importFrom = _interopRequireDefault(require("import-from"));

var _fileTypes = require("../file-types");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

async function mergeFileContent(file) {
  let content;

  for (const contentEntry of file.content) {
    // null values to remove the file have been already handled
    // previously, therefore target is a valid content value
    const targetContent = contentEntry;
    const mergeFunction = (0, _fileTypes.getMergeFunction)(file);

    if (typeof targetContent === "function") {
      // If content is a function, the type of the function needs to be
      // casted since TypeScript does not understand the correlation
      // between a file's type and its content yet.
      const targetContentFunction = targetContent;
      content = await targetContentFunction(content, mergeFunction);
    } else {
      // Merge based on the file type
      content = mergeFunction(content, targetContent);
    }
  } // Return null if file should not be placed


  if (content === null) {
    return null;
  }

  return { ...file,
    content: content
  };
}
/**
 * Merges all files with potential customizations that are available
 * in the current coat project.
 *
 * @param allFiles All grouped files that should be merged
 * @param context The context of the current coat project
 */


async function mergeFiles(allFiles, context) {
  // When the content property of a file is set to null
  // the file should not be placed.
  //
  // If this is the case, the file entry can be removed
  const allFilesFiltered = Object.values(allFiles).reduce((accumulator, file) => {
    const filteredContent = file.content.reduce((accumulator, contentEntry) => {
      if (contentEntry === null) {
        return [];
      }

      accumulator.push(contentEntry);
      return accumulator;
    }, []);

    if (filteredContent.length) {
      accumulator[file.file] = { ...file,
        content: filteredContent
      };
    }

    return accumulator;
  }, {}); // Gather customizations

  const filesWithCustomizationsGrouped = await Promise.all(Object.entries(allFilesFiltered).map(async ([filePath, file]) => {
    if (file.once) {
      // Files that should only be generated once should not
      // be customizable, because they can be edited directly
      return [filePath, file];
    } // Check whether a customization file exists
    //
    // TODO: See #35
    // Support additional customizations
    // like {file}-custom, {file}-custom.ts, {file}-custom.json (for JSON files)


    const customizationFilePath = `${filePath}-custom.js`;
    const relativeCustomizationFilePath = `./${_path.default.relative(context.cwd, customizationFilePath)}`;

    try {
      await _fs.promises.stat(customizationFilePath); // File exists
    } catch (error) {
      if (error.code === "ENOENT") {
        // File does not exist -> no customization
        return [filePath, file];
      } // File can't be accessed or there is another issue
      // TODO: See #15
      // Better error message


      throw error;
    } // Import the file and add to files array
    //
    // TODO: See #15
    // Catch error and show a nice error message that
    // requiring the customization file has errors
    // (e.g. due to parsing issues)


    const customizationFile = (0, _importFrom.default)(context.cwd, relativeCustomizationFilePath); // If the customizationFile exports null, the file should not be placed
    // on the filesystem. Return null, to filter out this fileGroup

    if (customizationFile === null) {
      return null;
    }

    const newFileContent = [...file.content, customizationFile];
    return [filePath, { ...file,
      content: newFileContent
    }];
  }));
  const mergedFiles = await Promise.all(filesWithCustomizationsGrouped.filter(entry => entry !== null).map(([, file]) => mergeFileContent(file)));
  const validFiles = mergedFiles.filter(file => file !== null);
  return validFiles;
}