"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.EventLogger = exports.EVENT_LOGGED_PREFIX = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _configSchema = require("@kbn/config-schema");
var _lodash = require("lodash");
var _semver = require("semver");
var _common = require("../common");
var _types = require("./types");
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */

class EventLogger {
  constructor(ctorParams) {
    (0, _defineProperty2.default)(this, "esContext", void 0);
    (0, _defineProperty2.default)(this, "eventLogService", void 0);
    (0, _defineProperty2.default)(this, "initialProperties", void 0);
    (0, _defineProperty2.default)(this, "systemLogger", void 0);
    this.esContext = ctorParams.esContext;
    this.eventLogService = ctorParams.eventLogService;
    this.initialProperties = ctorParams.initialProperties;
    this.systemLogger = ctorParams.systemLogger;
  }
  startTiming(event, startTime) {
    if (event == null) return;
    event.event = event.event || {};
    const start = startTime !== null && startTime !== void 0 ? startTime : new Date();
    event.event.start = start.toISOString();
  }
  stopTiming(event) {
    if ((event === null || event === void 0 ? void 0 : event.event) == null) return;
    const start = getEventStart(event);
    if (start == null || isNaN(start)) return;
    const end = Date.now();
    event.event.end = new Date(end).toISOString();
    event.event.duration = (0, _common.millisToNanos)(end - start);
  }

  // non-blocking, but spawns an async task to do the work
  logEvent(eventProperties) {
    var _coerce;
    const event = {};
    const fixedProperties = {
      ecs: {
        version: _types.ECS_VERSION
      },
      kibana: {
        server_uuid: this.eventLogService.kibanaUUID,
        version: (_coerce = (0, _semver.coerce)(this.eventLogService.kibanaVersion)) === null || _coerce === void 0 ? void 0 : _coerce.version
      }
    };
    const defaultProperties = {
      '@timestamp': new Date().toISOString()
    };

    // merge the initial properties and event properties
    (0, _lodash.merge)(event, defaultProperties, this.initialProperties, eventProperties, fixedProperties);
    let validatedEvent;
    try {
      validatedEvent = validateEvent(this.eventLogService, event);
    } catch (err) {
      this.systemLogger.warn(`invalid event logged: ${err.message}; ${JSON.stringify(event)})`);
      return;
    }
    const doc = {
      index: this.esContext.esNames.dataStream,
      body: validatedEvent
    };
    if (this.eventLogService.isIndexingEntries()) {
      indexEventDoc(this.esContext, doc);
    }
    if (this.eventLogService.isLoggingEntries()) {
      logEventDoc(this.systemLogger, doc);
    }
  }
}

// return the epoch millis of the start date, or null; may be NaN if garbage
exports.EventLogger = EventLogger;
function getEventStart(event) {
  var _event$event;
  if ((event === null || event === void 0 ? void 0 : (_event$event = event.event) === null || _event$event === void 0 ? void 0 : _event$event.start) == null) return null;
  return Date.parse(event.event.start);
}
const RequiredEventSchema = _configSchema.schema.object({
  provider: _configSchema.schema.string({
    minLength: 1
  }),
  action: _configSchema.schema.string({
    minLength: 1
  })
});
const ValidSavedObjectRels = new Set([undefined, _types.SAVED_OBJECT_REL_PRIMARY]);
function validateEvent(eventLogService, event) {
  var _result$kibana, _result$kibana$saved_;
  if ((event === null || event === void 0 ? void 0 : event.event) == null) {
    throw new Error(`no "event" property`);
  }

  // ensure there are provider/action properties in event as strings
  const requiredProps = {
    provider: event.event.provider,
    action: event.event.action
  };

  // will throw an error if structure doesn't validate
  const {
    provider,
    action
  } = RequiredEventSchema.validate(requiredProps);
  if (!eventLogService.isProviderActionRegistered(provider, action)) {
    throw new Error(`unregistered provider/action: "${provider}" / "${action}"`);
  }

  // could throw an error
  const result = _types.EventSchema.validate(event);
  if (result !== null && result !== void 0 && (_result$kibana = result.kibana) !== null && _result$kibana !== void 0 && (_result$kibana$saved_ = _result$kibana.saved_objects) !== null && _result$kibana$saved_ !== void 0 && _result$kibana$saved_.length) {
    for (const so of result === null || result === void 0 ? void 0 : (_result$kibana2 = result.kibana) === null || _result$kibana2 === void 0 ? void 0 : _result$kibana2.saved_objects) {
      var _result$kibana2;
      if (!ValidSavedObjectRels.has(so.rel)) {
        throw new Error(`invalid rel property in saved_objects: "${so.rel}"`);
      }
    }
  }
  return result;
}
const EVENT_LOGGED_PREFIX = `event logged: `;
exports.EVENT_LOGGED_PREFIX = EVENT_LOGGED_PREFIX;
function logEventDoc(logger, doc) {
  logger.info(`event logged: ${JSON.stringify(doc.body)}`);
}
function indexEventDoc(esContext, doc) {
  esContext.esAdapter.indexDocument(doc);
}