'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _chalk = require('chalk');

var _chalk2 = _interopRequireDefault(_chalk);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _stackTrace = require('stack-trace');

var _stackTrace2 = _interopRequireDefault(_stackTrace);

var _spinner = require('./helpers/spinner');

var _debug = require('../utils/debug');

var _debug2 = _interopRequireDefault(_debug);

var _printTools = require('../utils/print-tools');

var _dateUtils = require('../utils/date-utils');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

const { debug } = (0, _debug2.default)('cmd-trace');

class SocketTrace {
  constructor(context, spinner) {
    this.session = context.session;
    this.Socket = context.Socket;
    this.traceTimers = [];
    this.scriptWatch = false;
    this.lastId = {};

    if (spinner) {
      this.mainSpinner = spinner;
    } else {
      this.mainSpinner = new _spinner.GlobalSpinner((0, _printTools.p)(3)(`${_chalk2.default.grey('waiting...')}`));
    }
  }

  static getFirstTrace(errorMsg) {
    const err = new Error(errorMsg);
    return _lodash2.default.find(_stackTrace2.default.parse(err), o => o.lineNumber !== null);
  }

  run([socketName, cmd]) {
    var _this = this;

    return _asyncToGenerator(function* () {
      debug('SocketTrace run');
      _this.cmd = cmd;

      (0, _printTools.echo)(2)(`🔎 ${_chalk2.default.grey(' Tracing Socket calls')} ${_chalk2.default.dim('(Hit Ctrl-C to stop)')}`);
      (0, _printTools.echo)();

      if (socketName) {
        const msg = (0, _printTools.p)(3)(`${_chalk2.default.magenta('getting socket:')} ${(0, _dateUtils.currentTime)()}`);
        const spinner = new _spinner.SimpleSpinner(msg).start();
        const socket = yield _this.Socket.get(socketName);
        spinner.stop();
        return _this.startCollectingTraces(socket);
      }

      const msg = (0, _printTools.p)(3)(`${_chalk2.default.magenta('getting sockets:')} ${(0, _dateUtils.currentTime)()}`);
      const spinner = new _spinner.SimpleSpinner(msg).start();
      const sockets = yield _this.Socket.list();
      _this.mainSpinner.queueSize += sockets.length;
      spinner.stop();

      return Promise.all(sockets.map(function (socket) {
        return _this.startCollectingTraces(socket);
      }));
    })();
  }

  startCollectingTraces(socket) {
    this.mainSpinner.start();

    return socket.getTraces(this.lastId[socket.name]).then(response => {
      this.mainSpinner.stop();
      const trace = response.data;
      this.lastId[socket.name] = response.data.id;
      return this.printTrace(socket, trace);
    }).then(() => {
      this.startCollectingTraces(socket);
    }).catch(err => {
      this.mainSpinner.stop();
      if (err.code === 'ECONNABORTED') {
        this.startCollectingTraces(socket);
      } else {
        (0, _printTools.error)(4)(err.message);
      }
    });
  }

  // Decide about how to print trace and which
  printTrace(socket, trace) {
    var _this2 = this;

    return _asyncToGenerator(function* () {
      if (trace.payload.status === 'pending' || trace.payload.status === 'processing') return;

      const traceUrl = trace.payload.links.self;
      const fullTrace = yield _this2.Socket.getEndpointTraceByUrl(traceUrl);
      const { stdout, stderr, response } = fullTrace.result;

      if (trace.metadata.source === 'event_handler') {
        debug(`Event handler trace: ${trace.metadata.event_handler}`);
        const metadata = trace.metadata;
        const traceHandlerName = metadata.event_handler;
        SocketTrace.printTraceTriggerLine(fullTrace, metadata);
        if (stdout) SocketTrace.printTraceStdout(stdout);
        if (stderr) {
          (0, _printTools.echo)(6)(_chalk2.default.grey('script stderr:'));
          const errorMsg = fullTrace.result.stderr;

          try {
            const errorString = errorMsg.match(/\n(.*)\n\s.*at/)[1];
            const traceData = SocketTrace.getFirstTrace(errorMsg);
            const prettyTrace = socket.getPrettyTrace(traceData, traceHandlerName);
            SocketTrace.printFullTrace({ prettyTrace, errorString });
          } catch (err) {
            SocketTrace.printSimpleError(errorMsg);
          }
        }
      }

      if (trace.metadata.source === 'endpoint') {
        const traceFullEndpointName = trace.metadata.endpoint;
        const traceEndpointName = traceFullEndpointName.split('/')[1];

        SocketTrace.printTraceLine(fullTrace, traceFullEndpointName);
        if (stdout) SocketTrace.printTraceStdout(fullTrace.result.stdout);
        if (response && response.content) SocketTrace.printTraceBody(fullTrace.result.response);
        if (stderr) {
          (0, _printTools.echo)(6)(_chalk2.default.grey('script stderr:'));
          const errorMsg = fullTrace.result.stderr;

          try {
            const errorString = errorMsg.match(/\n(.*)\n\s.*at/)[1];
            const traceData = SocketTrace.getFirstTrace(errorMsg);

            const prettyTrace = socket.getPrettyTrace(traceData, traceEndpointName);
            SocketTrace.printFullTrace({ prettyTrace, errorString });
          } catch (err) {
            SocketTrace.printSimpleError(errorMsg);
          }
        }
      }
    })();
  }

  // Single line with date, id etc.
  static printTraceLine(trace, traceEndpointName) {
    const endpointCall = trace.status === 'success' ? _chalk2.default.grey('endpoint call:') : _chalk2.default.red('endpoint call:');
    const endpoint = _chalk2.default.cyan(traceEndpointName);
    const duration = _chalk2.default.dim(`${trace.duration} ms`);
    const remoteAddr = _chalk2.default.dim(`(${trace.meta.REMOTE_ADDR})`);
    const requestMethod = _chalk2.default.dim(trace.meta.REQUEST_METHOD);
    const exitCode = _chalk2.default.cyan(trace.result.response ? trace.result.response.status : '200');
    (0, _printTools.echo)(6)(`${endpointCall} ${(0, _dateUtils.currentTime)()} ${endpoint} ${requestMethod} ${exitCode} ${remoteAddr} ${duration}`);
  }

  // Single line with date, id etc.
  static printTraceTriggerLine(trace, metadata) {
    const eventName = metadata.event_handler;
    const endpointCall = trace.status === 'success' ? _chalk2.default.grey('handler call:') : _chalk2.default.red('handler call:');
    const event = _chalk2.default.cyan(eventName);
    const socketName = _chalk2.default.cyan(metadata.socket);
    const duration = _chalk2.default.dim(`${trace.duration} ms`);
    (0, _printTools.echo)(6)(`${endpointCall} ${(0, _dateUtils.currentTime)()} ${event} handled by ${socketName} ${duration}`);
  }

  static printTraceStdout(stdout, title = 'script stdout:') {
    (0, _printTools.echo)(6)(_chalk2.default.grey(title));
    (0, _printTools.echo)();
    stdout.split('\n').forEach(line => {
      (0, _printTools.echo)(14)(_chalk2.default.dim(line));
    });
    (0, _printTools.echo)();
  }

  static printTraceBody(response) {
    (0, _printTools.echo)(8)(_chalk2.default.grey(`script body: (${response.content_type})`));
    (0, _printTools.echo)();
    let code = response.content;
    try {
      code = (0, _printTools.printSourceCode)(response.content_type, response.content);
    } catch (err) {
      debug(err);
    }
    code.split('\n').forEach(line => {
      (0, _printTools.echo)(21)(line);
    });
    (0, _printTools.echo)();
  }

  static printFileLine(params) {
    (0, _printTools.echo)(21)([`${_chalk2.default.dim('at')} ${params.origFilePath}`, `${_chalk2.default.dim('line:')} ${params.lineNumber},`, `${_chalk2.default.dim('column:')} ${params.columnNumber}`].join(' '));
    (0, _printTools.echo)();
  }

  static printTraceLines(params) {
    (0, _printTools.echo)();
    params.lines.forEach(line => {
      (0, _printTools.echo)(21)(_chalk2.default.dim(line));
    });
    (0, _printTools.echo)(21)(params.errorString);
  }

  static printFullTrace(params) {
    SocketTrace.printTraceLines({
      lines: params.prettyTrace.lines,
      errorString: params.errorString
    });

    SocketTrace.printFileLine({
      origFilePath: params.prettyTrace.origFilePath,
      lineNumber: params.prettyTrace.lineNumber,
      columnNumber: params.prettyTrace.columnNumber
    });
  }

  static printSimpleError(simpleError) {
    (0, _printTools.echo)();
    const err = simpleError.split('\n').join(`\n${(0, _printTools.p)(21)()}`);
    (0, _printTools.echo)(21)(_chalk2.default.dim.red(err));
    (0, _printTools.echo)();
  }
}
exports.default = SocketTrace;
module.exports = exports['default'];
//# sourceMappingURL=socket-trace.js.map