/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

const visit = require('unist-util-visit');
const path = require('path');
const url = require('url');
const fs = require('fs-extra');
const escapeHtml = require('escape-html');
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
const {posixPath, toMessageRelativeFilePath} = require('@docusaurus/utils');

const {
  loaders: {inlineMarkdownImageFileLoader},
} = getFileLoaderUtils();

const createJSX = (node, pathUrl) => {
  const jsxNode = node;
  jsxNode.type = 'jsx';
  jsxNode.value = `<img ${node.alt ? `alt={"${escapeHtml(node.alt)}"} ` : ''}${
    node.url
      ? `src={require("${inlineMarkdownImageFileLoader}${pathUrl}").default}`
      : ''
  }${node.title ? ` title="${escapeHtml(node.title)}"` : ''} />`;

  if (jsxNode.url) {
    delete jsxNode.url;
  }
  if (jsxNode.alt) {
    delete jsxNode.alt;
  }
  if (jsxNode.title) {
    delete jsxNode.title;
  }
};

async function ensureImageFileExist(imagePath, sourceFilePath) {
  const imageExists = await fs.exists(imagePath);
  if (!imageExists) {
    throw new Error(
      `Image ${toMessageRelativeFilePath(
        imagePath,
      )} used in ${toMessageRelativeFilePath(sourceFilePath)} not found.`,
    );
  }
}

async function processImageNode(node, {filePath, staticDir}) {
  if (!node.url) {
    throw new Error(
      `Markdown image url is mandatory. filePath=${toMessageRelativeFilePath(
        filePath,
      )}`,
    );
  }

  const parsedUrl = url.parse(node.url);
  if (parsedUrl.protocol) {
    // pathname:// is an escape hatch,
    // in case user does not want his images to be converted to require calls going through webpack loader
    // we don't have to document this for now,
    // it's mostly to make next release less risky (2.0.0-alpha.59)
    if (parsedUrl.protocol === 'pathname:') {
      node.url = node.url.replace('pathname://', '');
    } else {
      // noop
    }
  }
  // images without protocol
  else if (path.isAbsolute(node.url)) {
    // absolute paths are expected to exist in the static folder
    const expectedImagePath = path.join(staticDir, node.url);
    await ensureImageFileExist(expectedImagePath, filePath);
    createJSX(node, posixPath(expectedImagePath));
  }
  // We try to convert image urls without protocol to images with require calls
  // going through webpack ensures that image assets exist at build time
  else {
    // relative paths are resolved against the source file's folder
    const expectedImagePath = path.join(path.dirname(filePath), node.url);
    await ensureImageFileExist(expectedImagePath, filePath);
    createJSX(node, node.url.startsWith('./') ? node.url : `./${node.url}`);
  }
}

const plugin = (options) => {
  const transformer = async (root) => {
    const promises = [];
    visit(root, 'image', (node) => {
      promises.push(processImageNode(node, options));
    });
    await Promise.all(promises);
  };
  return transformer;
};

module.exports = plugin;
