import process from "node:process";
import { existsSync } from "node:fs";
import path from "node:path";
import { exec } from "node:child_process";
import { fileURLToPath } from "node:url";
import type { IAppConfig } from "@digiresilience/metamigo-config";

/**
 * We use graphile-migrate for managing database migrations.
 *
 * However we also use convict as the sole source of truth for our app's configuration. We do not want to have to configure
 * separate env files or config files for graphile-migrate and yet again others for convict.
 *
 * So we wrap the graphile-migrate cli tool here. We parse our convict config, set necessary env vars, and then shell out to
 * graphile-migrate.
 *
 * Commander eats all args starting with --, so you must use the -- escape to indicate the arguments have finished
 *
 * Example:
 *      ./cli db -- --help        // will show graphile migrate help
 *      ./cli db -- watch         // will watch the current sql for changes
 *      ./cli db -- watch --once  // will apply the current sql once
 */
export const migrateWrapper = async (
  commands: string[],
  config: IAppConfig,
  silent = false
): Promise<void> => {
  const env = {
    DATABASE_URL: config.db.connection,
    SHADOW_DATABASE_URL: config.dev.shadowConnection,
    ROOT_DATABASE_URL: config.dev.rootConnection,
    DATABASE_NAME: config.db.name,
    DATABASE_OWNER: config.db.owner,
    DATABASE_AUTHENTICATOR: config.postgraphile.auth,
    DATABASE_VISITOR: config.postgraphile.visitor,
  };
  const cmd = `npx --no-install graphile-migrate ${commands.join(" ")}`;
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
  const dbDir = path.resolve(__dirname, "../../");
  const gmrcPath = path.resolve(__dirname, "../../.gmrc");
  if (!existsSync(gmrcPath)) {
    throw new Error(`graphile migrate config not found at ${gmrcPath}`);
  }

  if (!silent) console.log("executing:", cmd);

  return new Promise((resolve, reject) => {
    const proc = exec(cmd, {
      env: { ...process.env, ...env },
      cwd: dbDir,
    });

    proc.stdout.on("data", (data) => {
      if (!silent) console.log("MIGRATE:", data);
    });

    proc.stderr.on("data", (data) => {
      console.error("MIGRATE", data);
    });
    proc.on("close", (code) => {
      if (code !== 0) {
        reject(new Error(`graphile-migrate exited with code ${code}`));
        return;
      }

      resolve();
    });
  });
};
