/* eslint-disable @typescript-eslint/no-var-requires */
const http = require("http");
const crypto = require("crypto");
const log = require("electron-log");
const Config = require("electron-config");
const config = new Config();

let server;
let window;

async function initialize(win) {
  window = win;
  server = http.createServer(async function (req, res) {
    let body = "";
    res.setHeader("Content-Type", "application/json");

    req.on("data", (chunk) => {
      body += chunk.toString(); // convert Buffer to string
    });

    req.on("end", async () => {
      const providedToken = req.headers?.authorization?.replace("Bearer ", "") ?? "";
      const isValid = providedToken === getAuthenticationToken();
      if (isValid) {
        log.debug("Valid authentication token");
      } else {
        log.log("Invalid authentication token");
        res.writeHead(401);

        res.end(
          JSON.stringify({
            success: false,
            msg: "Invalid authentication token",
          }),
        );
        return;
      }

      let data;
      try {
        data = JSON.parse(body);
      } catch (error) {
        log.warn(`Invalid body data`);
        res.writeHead(400);
        res.end(
          JSON.stringify({
            success: false,
            msg: "Invalid body data",
          }),
        );

        return;
      }

      let result;
      switch (req.method) {
        // Request files
        case "GET":
          result = await window.webContents.executeJavaScript(`document.getFiles()`);
          break;

        // Create or update files
        // Support POST for VScode implementation
        case "POST":
        case "PUT":
          if (!data) {
            log.warn(`Invalid script update request - No data`);
            res.writeHead(400);
            res.end(
              JSON.stringify({
                success: false,
                msg: "Invalid script update request - No data",
              }),
            );
            return;
          }

          result = await window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`);
          break;

        // Delete files
        case "DELETE":
          result = await window.webContents.executeJavaScript(`document.deleteFile("${data.filename}")`);
          break;
      }

      if (!result.res) {
        //We've encountered an error
        res.writeHead(400);
        log.warn(`Api Server Error`, result.msg);
      }

      res.end(
        JSON.stringify({
          success: result.res,
          msg: result.msg,
          data: result.data,
        }),
      );
    });
  });

  const autostart = config.get("autostart", false);
  if (autostart) {
    try {
      await enable();
    } catch (error) {
      return Promise.reject(error);
    }
  }

  return Promise.resolve();
}

function enable() {
  if (isListening()) {
    log.warn("API server already listening");
    return Promise.resolve();
  }

  const port = config.get("port", 9990);
  const host = config.get("host", "127.0.0.1");
  log.log(`Starting http server on port ${port} - listening on ${host}`);

  // https://stackoverflow.com/a/62289870
  let startFinished = false;
  return new Promise((resolve, reject) => {
    server.listen(port, host, () => {
      if (!startFinished) {
        startFinished = true;
        resolve();
      }
    });
    server.once("error", (err) => {
      if (!startFinished) {
        startFinished = true;
        console.log("There was an error starting the server in the error listener:", err);
        reject(err);
      }
    });
  });
}

function disable() {
  if (!isListening()) {
    log.warn("API server not listening");
    return Promise.resolve();
  }

  log.log("Stopping http server");
  return server.close();
}

function toggleServer() {
  if (isListening()) {
    return disable();
  } else {
    return enable();
  }
}

function isListening() {
  return server?.listening ?? false;
}

function toggleAutostart() {
  const newValue = !isAutostart();
  config.set("autostart", newValue);
  log.log(`New autostart value is '${newValue}'`);
}

function isAutostart() {
  return config.get("autostart");
}

function getAuthenticationToken() {
  const token = config.get("token");
  if (token) return token;

  const newToken = generateToken();
  config.set("token", newToken);
  return newToken;
}

function generateToken() {
  const buffer = crypto.randomBytes(48);
  return buffer.toString("base64");
}

module.exports = {
  initialize,
  enable,
  disable,
  toggleServer,
  toggleAutostart,
  isAutostart,
  getAuthenticationToken,
  isListening,
};