#!/usr/bin/env node
import path from "node:path";
import { fileURLToPath } from "node:url";
import shelljs from "shelljs";
import fs from "fs-extra";
import faker from "faker";
import graphGenerator from "ngraph.generators";
import copy from "copy-template-dir";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
faker.seed(123);

const scope = `@${faker.hacker
  .noun()
  .toLowerCase()
  .replace(/\s/g, "-")
  .replace("1080p-", "rando")}`;

const type = process.argv[2];

// TODO: algo should be customizable along with the size
const packageGraph = graphGenerator.complete(5);

// Generate the package name & versions
packageGraph.forEachNode((node) => {
  node.data = {
    name: `${scope}/${faker.hacker.adjective()}-${faker.hacker.noun()}`
      .toLocaleLowerCase()
      .replace(/\s/g, "-"),
    version: faker.system.semver(),
  };
});

// Generate package dependencies
packageGraph.forEachNode((node) => {
  const links = packageGraph.getLinks(node.id);

  if (links) {
    for (const link of links) {
      if (link.fromId === node.id) {
        const depNode = packageGraph.getNode(link.toId);
        node.data.dependencies = node.data.dependencies || {};
        node.data.dependencies[depNode.data.name] = `^${depNode.data.version}`;
        node.data.implicitDependencies = node.data.implicitDependencies || [];
        node.data.implicitDependencies.push(
          depNode.data.name.replace(/^@[^/]+\//, "")
        );
      }
    }
  }
});

// Generate the monorepo
// 1. the root package.json
// 2. create packages/
// 3. create package directories
const root = path.join(__dirname, "..", "demo", type);

function generate(root, skipInstall) {
  fs.mkdirSync(root, { recursive: true });
  if (type !== "nx") {
    fs.writeFileSync(
      path.join(root, ".gitignore"),
      `node_modules
dist
.turbo
out
turbo
turbo-linux
.yalc`
    );
    if (fs.existsSync(root)) {
      try {
        fs.rmSync(root + "/packages", { recursive: true });
      } catch (error) {
        // do nothing
      }
    }

    let deps =
      type !== "turbo"
        ? {
            devDependencies: {
              [type]: "*",
            },
          }
        : {};

    fs.writeFileSync(
      path.join(root, "package.json"),
      JSON.stringify(
        {
          name: "monorepo",
          version: "0.0.0",
          private: true,
          workspaces: ["packages/*"],
          ...deps,
          packageManager: "yarn@1.22.17",
        },
        null,
        2
      )
    );

    fs.writeFileSync(
      path.join(root, "tsconfig.json"),
      JSON.stringify(
        {
          compilerOptions: {
            composite: false,
            declaration: true,
            declarationMap: true,
            esModuleInterop: true,
            forceConsistentCasingInFileNames: true,
            inlineSourceMap: true,
            inlineSources: false,
            isolatedModules: true,
            moduleResolution: "node",
            noUnusedLocals: false,
            noUnusedParameters: false,
            preserveWatchOutput: true,
            skipLibCheck: true,
            strict: true,
            lib: ["es2020"],
            module: "commonjs",
            target: "es2020",
          },
        },
        null,
        2
      )
    );
  }

  if (type === "turbo") {
    fs.writeFileSync(
      path.join(root, "turbo.json"),
      JSON.stringify(
        {
          npmClient: "yarn",
          cacheStorageConfig: {
            provider: "local",
            cacheUrl: "https://1a77600385dd.ngrok.io",
          },
          pipeline: {
            build: {
              outputs: ["dist/**/*"],
              dependsOn: ["^build"],
            },
            test: {
              dependsOn: ["build"],
            },
            dev: {
              cache: false,
            },
          },
        },
        null,
        2
      )
    );
  }

  if (type === "lerna") {
    fs.writeFileSync(
      path.join(root, "lerna.json"),
      JSON.stringify(
        {
          packages: ["packages/*"],
          version: "0.0.0",
        },
        null,
        2
      )
    );
  }

  if (type === "lage") {
    fs.writeFileSync(
      path.join(root, "lage.config.js"),
      `
module.exports = {
  pipeline: {
    build: ['^build'],
    test: ['build'],
    lint: [],
  },
  npmClient: 'yarn',
  cacheOptions: {
    cacheStorageConfig: {
      provider: 'local',
    },
    outputGlob: ['dist/**'],
  },
};
    `
    );
  }

  if (type !== "nx") {
    fs.mkdirSync(path.join(root, "packages"));
  } else {
    shelljs.exec(
      `cd ${path.join(
        __dirname,
        "../demo"
      )} && yarn create nx-workspace nx --preset=empty --nx-cloud=false --packageManager=yarn --cli=nx --linter=eslint`
    );
    shelljs.exec(`cd ${root} && yarn add @nrwl/node`);
  }

  if (type !== "nx") {
    packageGraph.forEachNode((node) => {
      const packageRoot = path.join(
        root,
        "packages",
        node.data.name.replace(/^@[^/]+\//, "")
      );
      fs.mkdirSync(packageRoot, { recursive: true });
      copy(
        path.join(__dirname, "templates"),
        path.join(packageRoot),
        {
          name: node.data.name.replace(/^@[^/]+\//, ""),
        },
        () => {}
      );

      fs.writeFileSync(
        path.join(packageRoot, "package.json"),
        JSON.stringify(
          {
            name: node.data.name,
            version: node.data.version,
            dependencies: node.data.dependencies,
            files: ["dist/**"],
            main: "dist/index.js",
            types: "dist/index.d.ts",
            devDependencies: {
              typescript: "^4.6.3",
              jest: "^27.0.0",
              "ts-jest": "^27.0.0",
              "@types/jest": "^27.0.0",
            },
            scripts: {
              build: "tsc",
              dev: "tsc -w",
              test: "jest",
            },
          },
          null,
          2
        )
      );
    });
  }

  if (type === "nx") {
    packageGraph.forEachNode((node) => {
      shelljs.exec(
        `cd ${root} && yarn nx g @nrwl/node:library --buildable --publishable --name="${node.data.name.replace(
          /^@[^/]+\//,
          ""
        )}" --importPath="${node.data.name.replace(/^@[^/]+\//, "")}"`
      );
      // instead of dealing with actual code, just list as implicitDependencies
      const safeName = node.data.name.replace(/^@[^/]+\//, "");
      const workspace = fs.readJSONSync(path.join(root, "workspace.json"));
      workspace.projects[safeName] = {
        ...workspace.projects[safeName],
        implicitDependencies: node.data.implicitDependencies || [],
      };
      fs.writeFileSync(
        path.join(root, "nx.json"),
        JSON.stringify(workspace, null, 2)
      );
    });
  }
  if (!skipInstall) {
    shelljs.exec(`cd ${root} && yarn install`);
  }
  fs.ensureDirSync(path.join(root, ".git"));
  fs.writeFileSync(
    path.join(root, ".git", "config"),
    `
[user]
	name = GitHub Actions
	email = actions@users.noreply.github.com
`
  );
  shelljs.exec(
    `cd ${root} && git init -q && git add . && git commit -m "init"`
  );
}

generate(root);
if (type === "turbo") {
  generate(root + "-installed", true);
}
