// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// @ts-check
const fs = require("fs");
const path = require("path");
const glob = require("glob");
const config = require("./config");
const { getMemoizedImportsForFile } = require("./import-finder");

// "Eligible" means "a file that we might want to list in tsconfig.strictNullChecks.json"
// "Checked" means "a file that is currently listed in tsconfig.strictNullChecks.json"
// It is possible for an ineligible file to be checked (eg, a png under /src/icons/**)

const isEligibleFile = (file) => !config.skippedFiles.has(path.relative(config.srcRoot, file));

function globAsync(pattern) {
  return new Promise((resolve, reject) => glob(pattern, (err, files) => (err ? reject(err) : resolve(files))));
}

// Includes both checked and unchecked files (ie, doesn't care about inclusion in tsconfig.strictNullChecks.json)
async function getAllEligibleFiles() {
  const tsFiles = await globAsync(`${config.srcRoot}/**/*.@(ts|tsx)`);
  return tsFiles.filter(isEligibleFile);
}

// Includes ineligible files that are listed under glob patterns in tsconfig.strictNullChecks
async function getAllCheckedFiles() {
  const tsconfigPath = path.join(config.repoRoot, config.targetTsconfig);
  const tsconfigContent = JSON.parse(fs.readFileSync(tsconfigPath).toString());

  const set = new Set(tsconfigContent.files.map((f) => path.join(config.repoRoot, f).replace(/\\/g, "/")));
  await Promise.all(
    tsconfigContent.include.map(async (include) => {
      const includePath = path.join(config.repoRoot, include);
      const files = await globAsync(includePath);
      for (const file of files) {
        set.add(file);
      }
    })
  );
  return set;
}

async function getUncheckedLeafFiles() {
  const checkedFiles = await getAllCheckedFiles();
  const eligibleFiles = await getAllEligibleFiles();
  const eligibleFileSet = new Set(eligibleFiles);
  const allUncheckedFiles = eligibleFiles.filter((file) => !checkedFiles.has(file));

  const areAllImportsChecked = (file) => {
    const allImports = getMemoizedImportsForFile(file, config.srcRoot);
    const uncheckedImports = allImports.filter((imp) => !checkedFiles.has(imp));
    const ineligibleUncheckedImports = uncheckedImports.filter((imp) => !eligibleFileSet.has(imp));
    if (ineligibleUncheckedImports.length > 0) {
      console.warn(
        `Eligible file ${file} with unchecked ineligible imports [${ineligibleUncheckedImports.join(", ")}]`
      );
    }
    return uncheckedImports.length === 0;
  };

  return allUncheckedFiles.filter(areAllImportsChecked);
}

module.exports = {
  getAllEligibleFiles,
  getUncheckedLeafFiles,
  getAllCheckedFiles,
};