/**
 * 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"; // RUNS UNTRANSFORMED IN NODE >= v4
// NO FANCY FEATURES, E.G. DESTRUCTURING, PLEASE!

const SourceMapConsumer = require("source-map").SourceMapConsumer;

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

const net = require("net");

process.once("message", socket => {
  net
    .createServer(
      {
        allowHalfOpen: true
      },
      connection => {
        connection.setEncoding("utf8");
        connection.pipe(
          concat(
            data => symbolicate(connection, data).catch(console.error) // log the error as a last resort
          )
        );
      }
    )
    .listen(socket, () => process.send(null));
});

function symbolicate(connection, data) {
  return Promise.resolve(data)
    .then(JSON.parse)
    .then(symbolicateStack)
    .then(JSON.stringify)
    .catch(makeErrorMessage)
    .then(message => connection.end(message));
}

function symbolicateStack(data) {
  const consumers = new Map(data.maps.map(mapToConsumer));
  return {
    result: data.stack.map(frame => mapFrame(frame, consumers))
  };
}

function mapFrame(frame, consumers) {
  const sourceUrl = frame.file;
  const consumer = consumers.get(sourceUrl);

  if (consumer == null) {
    return frame;
  }

  const original = consumer.originalPositionFor({
    line: frame.lineNumber,
    column: frame.column
  });

  if (!original) {
    return frame;
  }

  return Object.assign({}, frame, {
    file: original.source,
    lineNumber: original.line,
    column: original.column
  });
}

function makeErrorMessage(error) {
  return JSON.stringify({
    error: String((error && error.message) || error)
  });
}

function mapToConsumer(tuple) {
  tuple[1] = new SourceMapConsumer(tuple[1]);
  return tuple;
} // for testing

exports.symbolicate = symbolicate;