import { execa } from "execa";
import * as path from "path";
import { fileURLToPath, pathToFileURL } from "url";
import express from "express";
import bodyParser from "body-parser";
import * as crypto from "crypto";
import * as fs from "fs/promises";

const urlencodedParser = bodyParser.urlencoded();

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT_DIR = path.resolve(__dirname, "..");
const CLIENT_GENERATED_DIR = path.resolve(
  ROOT_DIR,
  "./client/src/__generated__"
);
const SERVER_GENERATED_DIR = path.resolve(
  ROOT_DIR,
  "./server/src/__generated__"
);

class QueryPersisterServer {
  static hash(input) {
    return crypto.createHash("md5").update(input).digest("hex");
  }

  constructor(port, queryJSONPath) {
    this.port = port;
    this.queryJSONPath = queryJSONPath;
    this.queryMap = {};
  }

  start() {
    return new Promise((resolve) => {
      const app = express();
      app.post("/", urlencodedParser, (req, res) => {
        const { text } = req.body;
        const id = QueryPersisterServer.hash(text);
        this.queryMap[id] = text;

        res.setHeader("Content-Type", "application/json");
        res.end(JSON.stringify({ id }));
      });
      this.server = app.listen(this.port, () => resolve());
    });
  }

  async flushAndClose() {
    const queryJSON = JSON.stringify(this.queryMap, null, 2);
    await fs.writeFile(this.queryJSONPath, queryJSON);
    if (this.server != null) {
      this.server.close();
    }
  }
}

async function clearGeneratedFiles() {
  await fs.rm(CLIENT_GENERATED_DIR, { recursive: true });
  await fs.mkdir(CLIENT_GENERATED_DIR);

  await fs.rm(SERVER_GENERATED_DIR, { recursive: true });
  await fs.mkdir(SERVER_GENERATED_DIR);
}

async function runRelayCompiler() {
  await execa("npx", ["relay-compiler"], {
    cwd: ROOT_DIR,
  });
}

export default async function compileRelay() {
  await clearGeneratedFiles();

  const server = new QueryPersisterServer(
    8080,
    path.resolve(SERVER_GENERATED_DIR, "./queries.json")
  );

  await server.start();
  await runRelayCompiler();

  await server.flushAndClose();
}

if (import.meta.url === pathToFileURL(process.argv[1]).toString()) {
  compileRelay();
}