/**
 * Generating translations keys:
 *
 * This code walk through all the files in client directory and its children
 * Get the all keys called by our makeT utils function
 * And add only the new one on our en.client.json file
 *
 */

const fs = require("fs");
const path = require("path");
const Parser = require("i18next-scanner").Parser;
const englishKeys = require("../static/locales/en.client.json");
const _ = require("lodash");

const parser = new Parser({
  keySeparator: "/",
  nsSeparator: null,
});

async function* walk(dirs) {
  for (const dir of dirs) {
    for await (const d of await fs.promises.opendir(dir)) {
      const entry = path.join(dir, d.name);
      if (d.isDirectory()) yield* walk([entry]);
      else if (d.isFile()) yield entry;
    }
  }
}

const customHandler = (fileName) => (key, options) => {
  const keyWithFile = `${fileName}/${key}`;
  if (Object.keys(options).includes("count") === true) {
    const keyOne = `${keyWithFile}_one`;
    const keyOther = `${keyWithFile}_other`;
    parser.set(keyOne, key);
    parser.set(keyOther, key);
  } else {
    parser.set(keyWithFile, key);
  }
};

function sort(obj) {
  if (typeof obj !== "object" || Array.isArray(obj))
    return obj;
  const sortedObject = {};
  const keys = Object.keys(obj).sort();
  keys.forEach(key => sortedObject[key] = sort(obj[key]));
  return sortedObject;
}

const getKeysFromFile = (filePath, fileName) => {
  const content = fs.readFileSync(filePath, "utf-8");
  parser.parseFuncFromString(
    content,
    {
      list: [
        "i18next.t",
        "t", // To match the file-level t function created with makeT
      ],
    },
    customHandler(fileName)
  );
  const keys = parser.get({ sort: true });
  return keys;
};

// It is highly desirable to retain existing order, to not generate
// unnecessary merges/conflicts, so we do a specialized merge.
function merge(target, scanned) {
  let merges = 0;
  for (const key of Object.keys(scanned)) {
    if (!(key in target)) {
      console.log("Merging key", {key});
      target[key] = scanned[key];
      merges++;
    } else if (typeof target[key] === 'object') {
      merges += merge(target[key], scanned[key]);
    } else if (scanned[key] !== target[key]) {
      if (!key.endsWith('_one')) {
        console.log("Value difference", {key, value: target[key]});
      }
    }
  }
  return merges;
}

// Look for keys that are listed in json file but not found in source
// code. These may be stale and need deleting in weblate.
function reportUnrecognizedKeys(originalKeys, foundKeys) {
  let unknowns = 0;
  for (const section of Object.keys(originalKeys)) {
    if (!(section in foundKeys)) {
      console.log("Unknown section found", {section});
      unknowns++;
    } else {
      for (const key of Object.keys(originalKeys[section])) {
        if (!(key in foundKeys[section])) {
          console.log("Unknown key found", {section, key});
          unknowns++;
        }
      }
    }
  }
  return unknowns;
}

async function walkTranslation(dirs) {
  const originalKeys = _.cloneDeep(englishKeys);
  for await (const p of walk(dirs)) {
    const { name } = path.parse(p);
    if (p.endsWith('.map')) { continue; }
    getKeysFromFile(p, name);
  }
  const keys = parser.get({ sort: true });
  const foundKeys = _.cloneDeep(keys.en.translation);
  const mergeCount = merge(englishKeys, sort(keys.en.translation));
  await fs.promises.writeFile(
    "static/locales/en.client.json",
    JSON.stringify(englishKeys, null, 4) + '\n',  // match weblate's default
    "utf-8"
  );
  // Now, print a report of unrecognized keys - candidates
  // for deletion in weblate.
  const unknownCount = reportUnrecognizedKeys(originalKeys, foundKeys);
  console.log(`Found ${unknownCount} unknown key(s).`);
  console.log(`Make ${mergeCount} merge(s).`);
}

walkTranslation(["_build/app/client", ...process.argv.slice(2)]);