/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 *
 * @format
 */
"use strict";

const concat = require("concat-stream");

const debug = require("debug")("Metro:Symbolication");

const net = require("net");

const temp = require("temp");

const xpipe = require("xpipe");

const _require = require("./util"),
  LazyPromise = _require.LazyPromise,
  LockingPromise = _require.LockingPromise;

const _require2 = require("child_process"),
  fork = _require2.fork;

const affixes = {
  prefix: "metro-symbolicate",
  suffix: ".sock"
};

const childPath = require.resolve("./worker");

exports.createWorker = () => {
  // There are issues with named sockets on windows that cause the connection to
  // close too early so run the symbolicate server on a random localhost port.
  const socket =
    process.platform === "win32" ? 34712 : xpipe.eq(temp.path(affixes));
  const child = new LockingPromise(new LazyPromise(() => startupChild(socket)));
  return (stack, sourceMaps) =>
    child
      .then(() => connectAndSendJob(socket, message(stack, sourceMaps)))
      .then(JSON.parse)
      .then(
        response =>
          "error" in response
            ? Promise.reject(new Error(response.error))
            : response.result
      );
};

function startupChild(socket) {
  const child = fork(childPath);
  return new Promise((resolve, reject) => {
    child.once("error", reject).once("message", () => {
      child.removeAllListeners();
      resolve(child);
    });
    child.send(socket);
  });
}

function connectAndSendJob(socket, data) {
  const job = new Promise((resolve, reject) => {
    debug("Connecting to worker");
    const connection = net.createConnection(socket);
    connection.setEncoding("utf8");
    connection.on("error", reject);
    connection.pipe(concat(resolve));
    debug("Sending data to worker");
    connection.end(data);
  });
  job.then(() => debug("Received response from worker"));
  return job;
}

function message(stack, sourceMaps) {
  return JSON.stringify({
    maps: Array.from(sourceMaps),
    stack
  });
}