/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

/* import-globals-from head.js */

/**
 * Reset all telemetry events.
 */
function setupTelemetryTest() {
  // Let's reset the counts.
  Services.telemetry.clearEvents();

  // Ensure no events have been logged
  const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS;
  const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
  ok(!snapshot.parent, "No events have been logged for the main process");
}
/* exported setupTelemetryTest */

/**
 * Check that the logged telemetry events exactly match the array of expected events.
 * Will compare the number of events, the event methods, and the event extras including
 * the about:debugging session id.
 */
function checkTelemetryEvents(expectedEvents, expectedSessionId) {
  const evts = readAboutDebuggingEvents();
  is(evts.length, expectedEvents.length, "Expected number of events");
  if (evts.length !== expectedEvents.length) {
    info("Expected:" + JSON.stringify(expectedEvents, null, 2));
    info("Got: " + JSON.stringify(evts, null, 2));
  }

  function _eventHasExpectedExtras(e, expectedEvent) {
    const expectedExtras = Object.keys(expectedEvent.extras);
    return expectedExtras.every(extra => {
      return e.extras[extra] === expectedEvent.extras[extra];
    });
  }

  for (const expectedEvent of expectedEvents) {
    const sameMethodEvents = evts.filter(
      e => e.method === expectedEvent.method
    );
    ok(
      !!sameMethodEvents.length,
      "Found event for method: " + expectedEvent.method
    );

    const sameExtrasEvents = sameMethodEvents.filter(e =>
      _eventHasExpectedExtras(e, expectedEvent)
    );
    Assert.strictEqual(
      sameExtrasEvents.length,
      1,
      "Found exactly one event matching the expected extras"
    );
    if (sameExtrasEvents.length === 0) {
      info(JSON.stringify(sameMethodEvents));
    }
    is(
      sameExtrasEvents[0].extras.session_id,
      expectedSessionId,
      "Select page event has the expected session"
    );
  }

  return evts;
}
/* exported checkTelemetryEvents */

/**
 * Retrieve the session id from an "open" event.
 * Note that calling this will "clear" all the events.
 */
function getOpenEventSessionId() {
  const openEvents = readAboutDebuggingEvents().filter(
    e => e.method === "open_adbg"
  );
  ok(!!openEvents[0], "Found an about:debugging open event");
  return openEvents[0].extras.session_id;
}
/* exported getOpenEventSessionId */

/**
 * Read all the pending events that have "aboutdebugging" as their object property.
 * WARNING: Calling this method also flushes/clears the events.
 */
function readAboutDebuggingEvents() {
  const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS;
  // Retrieve and clear telemetry events.
  const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
  // about:debugging events are logged in the parent process
  const parentEvents = snapshot.parent || [];

  return parentEvents
    .map(_toEventObject)
    .filter(e => e.object === "aboutdebugging");
}
/* exported getLoggedEvents */

/**
 * The telemetry event data structure is simply an array. This helper remaps the array to
 * an object with more user friendly properties.
 */
function _toEventObject(rawEvent) {
  return {
    // Category is typically devtools.main for us.
    category: rawEvent[1],
    // Method is the event's name (eg open, select_page etc...)
    method: rawEvent[2],
    // Object will usually be aboutdebugging for our tests
    object: rawEvent[3],
    // Value is usually empty for devtools events
    value: rawEvent[4],
    // Extras contain all the details of the event, including the session_id.
    extras: rawEvent[5],
  };
}
