import config from "./config.ts";
import { getBgVMManagers } from "./vm/utils.ts";
import express from "express";
import bodyParser from "body-parser";

const app = express();
const vmManagers = getBgVMManagers();

app.use(bodyParser.json());

Object.values(vmManagers).forEach((manager) => {
  manager?.runBackgroundJobs();
});

app.post("/assignVM", async (req, res) => {
  try {
    // Find a pool that matches the size and region requirements
    const pools = Object.values(vmManagers).filter((mgr) => {
      return (
        mgr.getIsLarge() === Boolean(req.body.isLarge) &&
        (mgr.getRegion() === req.body.region || !req.body.region)
      );
    });
    let vm = null;
    // Sequentially try each to give earlier pools preference
    // We might want to add the ability to load balance as well by randomly selecting between pools with same priority
    for (let pool of pools) {
      console.log(
        "try assignVM from pool:",
        pool.getPoolName(),
        req.body.roomId,
        req.body.uid,
      );
      vm = await pool.assignVM(req.body.roomId, req.body.uid);
      if (vm) {
        res.json(vm);
        return;
      }
    }
    res.json(null);
  } catch (e) {
    console.warn(e);
    res.status(500).end();
  }
});

app.post("/releaseVM", async (req, res) => {
  try {
    const pool =
      vmManagers[
        req.body.provider + (req.body.isLarge ? "Large" : "") + req.body.region
      ];
    if (req.body.id) {
      await pool?.resetVM(req.body.id, req.body.roomId);
    }
    res.end();
  } catch (e) {
    console.warn(e);
    res.status(500).end();
  }
});

app.get("/stats", async (req, res) => {
  const vmManagerStats: AnyDict = {};
  for (let [key, vmManager] of Object.entries(vmManagers)) {
    const availableVBrowsers = await vmManager?.getAvailableVBrowsers();
    const stagingVBrowsers = await vmManager?.getStagingVBrowsers();
    const size = await vmManager?.getCurrentSize();
    if (key && vmManager) {
      vmManagerStats[key] = {
        availableVBrowsers,
        stagingVBrowsers,
        bufferSize: vmManager?.getTargetBuffer(),
        // terminationVBrowsers,
        size,
      };
    }
  }
  res.json(vmManagerStats);
});

app.get("/isFreePoolFull", async (req, res) => {
  const freePools = Object.values(vmManagers).filter((mgr) => {
    return mgr?.getIsLarge() === false && mgr?.getLimitSize() > 0;
  });
  const fullResult = await Promise.all<Boolean>(
    freePools.map(async (freePool) => {
      let isFull = false;
      if (freePool) {
        const availableCount = await freePool.getAvailableCount();
        const limitSize = freePool?.getLimitSize() ?? 0;
        const currentSize = await freePool.getCurrentSize();
        isFull = Boolean(
          limitSize > 0 &&
          (Number(availableCount) === 0 ||
            Number(currentSize) - Number(availableCount) > limitSize * 0.95),
        );
      }
      return isFull;
    }),
  );
  const isFull = freePools.length && fullResult.every(Boolean);
  res.json({ isFull });
});

app.post("/updateSnapshot", async (req, res) => {
  const pool = vmManagers[req.body.provider + req.body.region];
  const result = await pool?.updateSnapshot();
  res.send(result?.toString() + "\n");
});

app.listen(config.VMWORKER_PORT, () => {
  console.log("vmWorker listening on %s", config.VMWORKER_PORT);
});
