/**
 * Copyright (c) 2017-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.
 */

function execute() {
  const CWD = process.cwd();
  const fs = require("fs-extra");
  const readMetadata = require("./readMetadata.js");
  const renderToStaticMarkup = require("react-dom/server").renderToStaticMarkup;
  const path = require("path");
  const toSlug = require("../core/toSlug.js");
  const React = require("react");
  const mkdirp = require("mkdirp");
  const glob = require("glob");
  const Site = require("../core/Site.js");
  const siteConfig = require(CWD + "/siteConfig.js");
  const translate = require("./translate.js");
  const versionFallback = require("./versionFallback.js");

  const ENABLE_TRANSLATION = fs.existsSync(CWD + "/languages.js");
  const ENABLE_VERSIONING = fs.existsSync(CWD + "/versions.json");

  let languages;
  if (ENABLE_TRANSLATION) {
    languages = require(CWD + "/languages.js");
  } else {
    languages = [
      {
        enabled: true,
        name: "English",
        tag: "en"
      }
    ];
  }

  function writeFileAndCreateFolder(file, content) {
    mkdirp.sync(file.replace(new RegExp("/[^/]*$"), ""));

    fs.writeFileSync(file, content);
  }

  const TABLE_OF_CONTENTS_TOKEN = "<AUTOGENERATED_TABLE_OF_CONTENTS>";

  const insertTableOfContents = rawContent => {
    const regexp = /\n###\s+(`.*`.*)\n/g;
    let match;
    const headers = [];
    while ((match = regexp.exec(rawContent))) {
      headers.push(match[1]);
    }

    const tableOfContents = headers
      .map(header => `  - [${header}](#${toSlug(header)})`)
      .join("\n");

    return rawContent.replace(TABLE_OF_CONTENTS_TOKEN, tableOfContents);
  };

  function isSeparateCss(file) {
    if (!siteConfig.separateCss) {
      return false;
    }
    for (let i = 0; i < siteConfig.separateCss.length; i++) {
      if (file.includes(siteConfig.separateCss[i])) {
        return true;
      }
    }
    return false;
  }

  console.log("generate.js triggered...");

  const enabledLanguages = [];
  languages.filter(lang => lang.enabled).map(lang => {
    enabledLanguages.push(lang.tag);
  });

  readMetadata.generateDocsMetadata();
  const Metadata = require("../core/metadata.js");

  const mdToHtml = {};
  Object.keys(Metadata).forEach(id => {
    const metadata = Metadata[id];
    if (metadata.language !== "en" || metadata.original_id) {
      return;
    }
    let htmlLink =
      siteConfig.baseUrl + metadata.permalink.replace("/next/", "/");
    if (htmlLink.includes("/docs/en/")) {
      htmlLink = htmlLink.replace("/docs/en/", "/docs/en/VERSION/");
    } else {
      htmlLink = htmlLink.replace("/docs/", "/docs/VERSION/");
    }
    mdToHtml[metadata.source] = htmlLink;
  });

  const DocsLayout = require("../core/DocsLayout.js");

  fs.removeSync(CWD + "/build");

  // create html files for all docs
  Object.keys(Metadata).forEach(id => {
    const metadata = Metadata[id];

    let file;
    if (metadata.original_id) {
      if (ENABLE_TRANSLATION && metadata.language !== "en") {
        file =
          CWD + "/translated_docs/" + metadata.language + "/" + metadata.source;
      } else {
        file = CWD + "/versioned_docs/" + metadata.source;
      }
    } else {
      if (metadata.language === "en") {
        file = CWD + "/../docs/" + metadata.source;
      } else {
        file =
          CWD + "/translated_docs/" + metadata.language + "/" + metadata.source;
      }
    }

    if (!fs.existsSync(file)) {
      return;
    }

    let rawContent = readMetadata.extractMetadata(fs.readFileSync(file, "utf8"))
      .rawContent;

    const language = metadata.language;

    /* generate table of contents if appropriate */
    if (rawContent && rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) != -1) {
      rawContent = insertTableOfContents(rawContent);
    }

    let latestVersion;
    if (ENABLE_VERSIONING) {
      latestVersion = JSON.parse(
        fs.readFileSync(CWD + "/versions.json", "utf8")
      )[0];
    }

    /* replace any links to markdown files to their website html links */
    Object.keys(mdToHtml).forEach(function(key, index) {
      let link = mdToHtml[key];
      link = link.replace("/en/", "/" + language + "/");
      link = link.replace(
        "/VERSION/",
        metadata.version && (metadata.version !== latestVersion)
          ? "/" + metadata.version + "/"
          : "/"
      );
      rawContent = rawContent.replace(new RegExp(key, "g"), link);
    });

    rawContent = rawContent.replace(
      /\]\(assets\//g,
      "](" + siteConfig.baseUrl + "docs/assets/"
    );

    const docComp = (
      <DocsLayout metadata={metadata} language={language} config={siteConfig}>
        {rawContent}
      </DocsLayout>
    );
    const str = renderToStaticMarkup(docComp);
    const targetFile =
      CWD + "/build/" + siteConfig.projectName + "/" + metadata.permalink;

    writeFileAndCreateFolder(targetFile, str);
  });

  /* copy docs assets if they exist */
  if (fs.existsSync(CWD + "/../docs/assets")) {
    fs.copySync(
      CWD + "/../docs/assets",
      CWD + "/build/" + siteConfig.projectName + "/docs/assets"
    );
  }

  // create html files for all blog posts
  if (fs.existsSync(__dirname + "../core/MetadataBlog.js")) {
    fs.removeSync(__dirname + "../core/MetadataBlog.js");
  }
  readMetadata.generateBlogMetadata();
  const MetadataBlog = require("../core/MetadataBlog.js");
  const BlogPostLayout = require("../core/BlogPostLayout.js");

  files = glob.sync(CWD + "/blog/**/*.*");
  files.sort().reverse().forEach(file => {
    const extension = path.extname(file);
    if (extension !== ".md" && extension !== ".markdown") {
      return;
    }

    /* convert filename ot use slashes */
    const filePath = path
      .basename(file)
      .replace("-", "/")
      .replace("-", "/")
      .replace("-", "/")
      .replace(/\./g, "-")
      .replace(/\-md$/, ".html");
    const result = readMetadata.extractMetadata(
      fs.readFileSync(file, { encoding: "utf8" })
    );
    const rawContent = result.rawContent;
    const metadata = Object.assign(
      { path: filePath, content: rawContent },
      result.metadata
    );
    metadata.id = metadata.title;

    let language = "en";
    const blogPostComp = (
      <BlogPostLayout
        metadata={metadata}
        language={language}
        config={siteConfig}
      >
        {rawContent}
      </BlogPostLayout>
    );
    const str = renderToStaticMarkup(blogPostComp);

    let targetFile =
      CWD + "/build/" + siteConfig.projectName + "/blog/" + filePath;
    writeFileAndCreateFolder(targetFile, str);
  });
  // create html files for all blog pages
  const BlogPageLayout = require("../core/BlogPageLayout.js");
  const perPage = 10;
  for (let page = 0; page < Math.ceil(MetadataBlog.length / perPage); page++) {
    let language = "en";
    const metadata = { page: page, perPage: perPage };
    const blogPageComp = (
      <BlogPageLayout
        metadata={metadata}
        language={language}
        config={siteConfig}
      />
    );
    const str = renderToStaticMarkup(blogPageComp);

    let targetFile =
      CWD +
      "/build/" +
      siteConfig.projectName +
      "/blog" +
      (page > 0 ? "/page" + (page + 1) : "") +
      "/index.html";
    writeFileAndCreateFolder(targetFile, str);
  }

  /* copy blog assets if they exist */
  if (fs.existsSync(CWD + "/blog/assets")) {
    fs.copySync(
      CWD + "/blog/assets",
      CWD + "/build/" + siteConfig.projectName + "/blog/assets"
    );
  }

  /* copy all static files from docusaurus */
  files = glob.sync(__dirname + "/../static/**");
  files.forEach(file => {
    let targetFile =
      CWD +
      "/build/" +
      siteConfig.projectName +
      "/" +
      file.split("/static/")[1];
    if (file.match(/\.css$/)) {
      let cssContent = fs.readFileSync(file);
      cssContent = cssContent
        .toString()
        .replace(
          new RegExp("{primaryColor}", "g"),
          siteConfig.colors.primaryColor
        );
      cssContent = cssContent.replace(
        new RegExp("{secondaryColor}", "g"),
        siteConfig.colors.secondaryColor
      );
      cssContent = cssContent.replace(
        new RegExp("{prismColor}", "g"),
        siteConfig.colors.prismColor
      );

      mkdirp.sync(targetFile.replace(new RegExp("/[^/]*$"), ""));
      fs.writeFileSync(targetFile, cssContent);
    } else if (!fs.lstatSync(file).isDirectory()) {
      mkdirp.sync(targetFile.replace(new RegExp("/[^/]*$"), ""));
      fs.copySync(file, targetFile);
    }
  });

  /* copy all static files from user */
  files = glob.sync(CWD + "/static/**");
  files.forEach(file => {
    if (file.match(/\.css$/) && !isSeparateCss(file)) {
      const mainCss =
        CWD + "/build/" + siteConfig.projectName + "/css/main.css";
      let cssContent = fs.readFileSync(file);
      cssContent = fs.readFileSync(mainCss) + "\n" + cssContent;

      cssContent = cssContent
        .toString()
        .replace(
          new RegExp("{primaryColor}", "g"),
          siteConfig.colors.primaryColor
        );
      cssContent = cssContent.replace(
        new RegExp("{secondaryColor}", "g"),
        siteConfig.colors.secondaryColor
      );
      cssContent = cssContent.replace(
        new RegExp("{prismColor}", "g"),
        siteConfig.colors.prismColor
      );

      fs.writeFileSync(mainCss, cssContent);
    } else if (!fs.lstatSync(file).isDirectory()) {
      let parts = file.split("static");
      let targetFile =
        CWD + "/build/" + siteConfig.projectName + "/" + parts[1];
      mkdirp.sync(targetFile.replace(new RegExp("/[^/]*$"), ""));
      fs.copySync(file, targetFile);
    }
  });

  /* compile/copy pages from user */
  files = glob.sync(CWD + "/pages/**");
  files.forEach(file => {
    if (file.match(/\.js$/)) {
      /* make temp file for sake of require paths */
      const parts = file.split("pages");
      let tempFile = __dirname + "/../pages" + parts[1];
      tempFile = tempFile.replace(
        path.basename(file),
        "temp" + path.basename(file)
      );
      mkdirp.sync(tempFile.replace(new RegExp("/[^/]*$"), ""));
      fs.copySync(file, tempFile);

      const ReactComp = require(tempFile);

      let targetFile =
        CWD + "/build/" + siteConfig.projectName + "/" + parts[1];
      targetFile = targetFile.replace(/\.js$/, ".html");

      const regexLang = /\/pages\/(.*)\//;
      const match = regexLang.exec(file);
      const langParts = match[1].split("/");
      if (langParts.indexOf("en") !== -1) {
        /* copy and compile a page for each enabled language from the English file */
        for (let i = 0; i < enabledLanguages.length; i++) {
          let language = enabledLanguages[i];
          /* skip conversion from english file if a file exists for this language */
          if (
            language !== "en" &&
            fs.existsSync(file.replace("/en/", "/" + language + "/"))
          ) {
            continue;
          }
          translate.setLanguage(language);
          const str = renderToStaticMarkup(
            <Site language={language} config={siteConfig}>
              <ReactComp language={language} />
            </Site>
          );
          writeFileAndCreateFolder(
            targetFile.replace("/en/", "/" + language + "/"),
            str
          );
        }
      } else {
        /* allow for rendering of other files not in pages/en folder */
        let language = "en";
        for (let i = 0; i < langParts.length; i++) {
          if (enabledLanguages.indexOf(langParts[i]) !== -1) {
            language = langParts[i];
          }
        }
        translate.setLanguage(language);
        const str = renderToStaticMarkup(
          <Site language={language} config={siteConfig}>
            <ReactComp language={language} />
          </Site>
        );
        writeFileAndCreateFolder(targetFile, str);
      }

      fs.removeSync(tempFile);
    } else if (!fs.lstatSync(file).isDirectory()) {
      let parts = file.split("pages");
      let targetFile =
        CWD + "/build/" + siteConfig.projectName + "/" + parts[1];
      mkdirp.sync(targetFile.replace(new RegExp("/[^/]*$"), ""));
      fs.copySync(file, targetFile);
    }
  });

  /* copy html files in 'en' to base level as well */
  files = glob.sync(CWD + "/build/" + siteConfig.projectName + "/en/**");
  files.forEach(file => {
    let targetFile = file.replace("en/", "");
    if (file.match(/\.html$/)) {
      fs.copySync(file, targetFile);
    }
  });
}

module.exports = execute;