/**
 * More about the structure of .po files:
 * // https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#PO-Files
 *
 * ```po
 * white-space
 * #  translator-comments
 * #. extracted-comments
 * #: reference…
 * #, flag…
 * #| msgid previous-untranslated-string
 * msgid untranslated-string
 * msgstr translated-string
 * ```
 */

const { getParsedVueFiles } = require("./parse-vue-files")

const PARSED_VUE_FILES = getParsedVueFiles("**/*.?(js|vue)")

/** @param str {string} */
const escapeQuotes = (str) => str.replace(/"/g, '\\"')

/** @param str {string} */
const containsCurlyWord = (str) => /\{[a-zA-Z-]*}/.test(str)

/** @param str {string} */
const checkStringForVars = (str) =>
  containsCurlyWord(str) ? "#. Do not translate words between ### ###." : ""

/**
 * For GlotPress to display warning when the translators miss the placeholders
 * or try replacing them with something else, we need to surround the
 * placeholders with `###`.
 *
 * @param str {string} the translation string
 * @return {string} the translation string with all placeholders marked
 */
const replaceVarsPlaceholders = (str) => {
  if (!containsCurlyWord(str)) return str

  const variable = /\{(?<variable>[a-zA-Z-]*)}/g
  return str.replace(variable, `###$<variable>###`)
}

/**
 * Replace placeholder format for variables and escape quotes.
 *
 * @param str {string} the translation string
 * @return {string} the translation string with quotes escaped and placeholders marked
 */
const processValue = (str) => escapeQuotes(replaceVarsPlaceholders(str))

/**
 * Returns a comment with all reference to the file and line where the string is
 * used. These are prefixed with `#:`.
 *
 * @param  keyPath {string} the lineage of the entry to search in Vue files
 * @return {string[]} the list of reference comments
 */
const getRefComments = (keyPath) =>
  PARSED_VUE_FILES.filter((k) => k.path === keyPath).map(
    (item) => `#: ${item.file}:${item.line}`
  )

const pot_creation_date = () =>
  `${new Date().toISOString().split(".")[0]}+00:00`

const POT_FILE_META = `# Copyright (C) 2021
# This file is distributed under the same license as Openverse.
msgid ""
msgstr ""
"Project-Id-Version: Openverse \\n"
"Report-Msgid-Bugs-To: https://github.com/wordpress/openverse/issues \\n"
"POT-Creation-Date: ${pot_creation_date()}\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"PO-Revision-Date: 2021-MO-DA HO:MI+ZONE\\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
"Language-Team: LANGUAGE <LL@li.org>\\n"
`

/**
 * Generate the comment for the POT entry. This includes any comment written on
 * the JSON entry, a message about `###` and finally references to where that
 * entry is used in the codebase.
 *
 * @param entry {import('./read-i18n').Entry} the entry to get the comment for
 * @return {string} the comment lines
 */
const getComment = (entry) => {
  const comment = []

  // comments given by the programmer, directed at the translator (#.)
  if (entry.doc) comment.push(`#. ${entry.doc}`)

  // comments given by the programmer, directed at the translator (#.)
  let vars = checkStringForVars(entry.value)
  if (vars) comment.push(vars)

  // comments containing references to the program’s source code (#:)
  let refComments = getRefComments(entry.lineage)
  if (refComments.length) comment.push(...refComments)

  return comment.map((item) => `${item}`).join("\n")
}

/**
 * Convert a JSON entry into a POT entry. If the JSON entry has nested entries,
 * recursively convert them as well.
 *
 * @param entry {import('./read-i18n').Entry} the entry to convert to POT
 * @return {string} the POT equivalent of the JSON entry
 */
const toPot = (entry) => {
  if (!entry.value) {
    // string-object type mapping
    return entry.children.map((child) => toPot(child)).join("\n\n")
  }

  // string-string type mapping
  let poEntry = []
  let comment = getComment(entry)
  if (comment) poEntry.push(comment)
  poEntry.push(`msgctxt "${entry.lineage}"`)
  if (entry.value.includes("|") && /(count|time)/i.test(entry.value)) {
    const pluralizedValues = entry.value.split("|")
    if (pluralizedValues.length === 1) {
      pluralizedValues.push(pluralizedValues[0])
    }
    poEntry.push(
      `msgid "${processValue(pluralizedValues[0])}"`,
      `msgid_plural "${processValue(pluralizedValues[1])}"`,
      'msgstr[0] ""',
      'msgstr[1] ""'
    )
  } else {
    poEntry.push(`msgid "${processValue(entry.value)}"`, 'msgstr ""')
  }
  return poEntry.join("\n")
}

/**
 * Given the root entry generated by `read-i18n.parseJson`, this function
 * returns the complete text to output to the Openverse POT file.
 *
 * @param entry {import('./read-i18n').Entry} the root entry of the JSON file
 * @return {string} the text content of the Openverse POT file
 */
const makePot = (entry) => [POT_FILE_META, toPot(entry)].join("\n")

module.exports = { replaceVarsPlaceholders, makePot }
