#!/usr/bin/env node
import { readdirSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
import yargs from "yargs";
import watch from "node-watch";
import chalk from "chalk";

const argv = yargs
  .options({
    dir: {
      alias: "d",
      demandOption: true,
      type: "string",
      description: "Directory to generate barrels for",
    },
    watch: {
      alias: "w",
      type: "boolean",
      description: "Watch directory and update barrels",
    },
    ext: {
      alias: "e",
      type: "string",
      description:
        "Extension to create the barrel file with, auto-detected by default",
    },
  })
  .parseSync();

function joinPath(...path: string[]) {
  return join(process.cwd(), ...path);
}

function makeBarrels(watch: boolean, dir?: string, log?: boolean) {
  const dirFiles = readdirSync(joinPath(dir || argv.dir));
  const extension =
    argv.ext || (dirFiles.join(",").includes(".ts") ? "ts" : "js");
  const comments = `// AUTO GENERATED BY JS-BARRELS`;

  if (dirFiles.includes(`index.${extension}`)) {
    const fileContents = readFileSync(
      joinPath(
        dir || argv.dir,
        dirFiles.find((f) => f === `index.${extension}`)!
      )
    ).toString();
    if (!fileContents.startsWith(comments)) {
      console.log(
        chalk.bgRed("\n ERROR "),
        chalk.red(
          "Directory already contains an index file not generated by js-barrels."
        )
      );
      process.exit();
    }
  }

  type Export = {
    default: boolean;
    name: string;
    fileName: string;
  };

  const e: Export[] = [];

  for (const file of dirFiles) {
    if (file.includes(".")) {
      const fileContents = readFileSync(
        joinPath(dir || argv.dir, file)
      ).toString();
      if (
        (fileContents.includes("export") ||
          file.includes("svelte") ||
          file.includes("vue")) &&
        !file.includes("index")
      ) {
        let stringToSearch = "export default";

        if (file.includes("svelte") || file.includes("vue")) {
          e.push({
            default: true,
            name: file.split(".")[0],
            fileName: `./${file}`,
          });
        } else {
          if (fileContents.includes(stringToSearch)) {
            const contents = fileContents
              .substring(
                fileContents.indexOf(stringToSearch),
                fileContents.length
              )
              .split("\n")[0];

            const filteredContents = contents.substring(
              stringToSearch.length + 1,
              contents.length
            );

            if (
              filteredContents.startsWith("function") ||
              filteredContents.startsWith("interface")
            ) {
              let keyword = filteredContents.startsWith("function")
                ? "function"
                : "interface";

              const id = filteredContents.substring(
                keyword.length + 1,
                filteredContents.length
              );

              const token =
                keyword === "function"
                  ? id.substring(0, id.indexOf("("))
                  : id.substring(0, id.indexOf(" "));

              e.push({
                default: true,
                name: token,
                fileName: `./${
                  file.includes("ts") || file.includes("js")
                    ? file.split(".")[0]
                    : file
                }`,
              });
            }
          }
          if (fileContents.includes("export"))
            e.push({
              default: false,
              name: "",
              fileName: `./${
                file.includes("ts") || file.includes("js")
                  ? file.split(".")[0]
                  : file
              }`,
            });
        }
      }
    } else {
      makeBarrels(false, join(argv.dir, file), true);
    }
  }

  const generatedFile = `${comments}
${e
  .map((ex) =>
    ex.default
      ? `export { default as ${ex.name} } from "${ex.fileName}";`
      : `export * from "${ex.fileName}";`
  )
  .join("\n")}`;
  writeFileSync(joinPath(dir || argv.dir, `index.${extension}`), generatedFile);
  if (watch) {
    console.log(
      chalk.bgYellow(" FILE SAVED "),
      chalk.yellow("Barrel files regenerated.")
    );
  } else if (!log) {
    console.log(
      "\n" + chalk.bgGreen(" SUCCESS "),
      chalk.green("Barrel files generated.\n")
    );
  }
}

if (argv.watch) {
  makeBarrels(false);
  watch(
    argv.dir,
    {
      recursive: false,
      filter: (f) => !f.includes("index.ts") && !f.includes("index.js"),
    },
    () => {
      makeBarrels(true);
    }
  );
} else {
  makeBarrels(false);
}
