import {devAssert, devAssertString} from '#core/assert';
import {internalListenImplementation} from '#core/dom/event-helper-listen';
import {rethrowAsync} from '#core/error';
import {tryParseJson} from '#core/types/object/json';

/** @const */
const AMP_MESSAGE_PREFIX = 'amp-';
export const CONSTANTS = {
  responseTypeSuffix: '-result',
  messageIdFieldName: 'messageId',
  payloadFieldName: 'payload',
  contentFieldName: 'content',
};

/** @enum {string} */
export const MessageType_Enum = {
  // For amp-ad
  SEND_EMBED_STATE: 'send-embed-state',
  EMBED_STATE: 'embed-state',
  SEND_EMBED_CONTEXT: 'send-embed-context',
  EMBED_CONTEXT: 'embed-context',
  SEND_INTERSECTIONS: 'send-intersections',
  INTERSECTION: 'intersection',
  EMBED_SIZE: 'embed-size',
  EMBED_SIZE_CHANGED: 'embed-size-changed',
  EMBED_SIZE_DENIED: 'embed-size-denied',
  NO_CONTENT: 'no-content',
  GET_HTML: 'get-html',
  GET_CONSENT_STATE: 'get-consent-state',
  SIGNAL_INTERACTIVE: 'signal-interactive',

  // For the frame to be placed in full overlay mode for lightboxes
  FULL_OVERLAY_FRAME: 'full-overlay-frame',
  FULL_OVERLAY_FRAME_RESPONSE: 'full-overlay-frame-response',
  CANCEL_FULL_OVERLAY_FRAME: 'cancel-full-overlay-frame',
  CANCEL_FULL_OVERLAY_FRAME_RESPONSE: 'cancel-full-overlay-frame-response',

  // For amp-inabox
  SEND_POSITIONS: 'send-positions',
  POSITION: 'position',

  // For amp-analytics' iframe-transport
  SEND_IFRAME_TRANSPORT_EVENTS: 'send-iframe-transport-events',
  IFRAME_TRANSPORT_EVENTS: 'iframe-transport-events',
  IFRAME_TRANSPORT_RESPONSE: 'iframe-transport-response',

  // For user-error-in-iframe
  USER_ERROR_IN_IFRAME: 'user-error-in-iframe',

  // For amp-iframe
  SEND_CONSENT_DATA: 'send-consent-data',
  CONSENT_DATA: 'consent-data',
};

/**
 * Listens for the specified event on the element.
 * @param {EventTarget} element
 * @param {string} eventType
 * @param {function(Event):void} listener
 * @param {Object=} opt_evtListenerOpts
 * @return {import('#core/types/function/types').UnlistenCallback}
 */
export function listen(element, eventType, listener, opt_evtListenerOpts) {
  return internalListenImplementation(
    element,
    eventType,
    listener,
    opt_evtListenerOpts
  );
}

/**
 * Serialize an AMP post message. Output looks like:
 * 'amp-011481323099490{"type":"position","sentinel":"12345","foo":"bar"}'
 * @param {string} type
 * @param {string} sentinel
 * @param {JsonObject=} data
 * @param {?string=} rtvVersion
 * @return {string}
 */
export function serializeMessage(type, sentinel, data = {}, rtvVersion = null) {
  // TODO: consider wrap the data in a "data" field. { type, sentinel, data }
  const message = data;
  message['type'] = type;
  message['sentinel'] = sentinel;
  return AMP_MESSAGE_PREFIX + (rtvVersion || '') + JSON.stringify(message);
}

/**
 * Deserialize an AMP post message.
 * Returns null if it's not valid AMP message format.
 *
 * @param {*} message
 * @return {?JsonObject|undefined}
 */
export function deserializeMessage(message) {
  if (!isAmpMessage(message)) {
    return null;
  }

  devAssertString(message);

  const startPos = message.indexOf('{');
  devAssert(startPos != -1, 'JSON missing in %s', message);
  return tryParseJson(message.substr(startPos), (e) => {
    rethrowAsync(
      new Error(`MESSAGING: Failed to parse message: ${message}\n${e.message}`)
    );
  });
}

/**
 *  Returns true if message looks like it is an AMP postMessage
 *  @param {*} message
 *  @return {boolean}
 */
export function isAmpMessage(message) {
  return (
    typeof message == 'string' &&
    message.startsWith(AMP_MESSAGE_PREFIX) &&
    message.indexOf('{') != -1
  );
}

/** @typedef {{creativeId: string, message: string}} IframeTransportEventDef */
// An event, and the transport ID of the amp-analytics tags that
// generated it. For instance if the creative with transport
// ID 2 sends "hi", then an IframeTransportEventDef would look like:
// { creativeId: "2", message: "hi" }
// If the creative with transport ID 2 sent that, and also sent "hello",
// and the creative with transport ID 3 sends "goodbye" then an *array* of 3
// IframeTransportEventDef would be sent to the 3p frame like so:
// [
//   { creativeId: "2", message: "hi" }, // An IframeTransportEventDef
//   { creativeId: "2", message: "hello" }, // Another
//   { creativeId: "3", message: "goodbye" } // And another
// ]
