"use strict";
/* eslint-disable mozilla/reject-some-requires */
const { Ci } = require("chrome");
const { KeyCodes } = require("devtools/client/shared/keycodes");
const { Task } = require("devtools/shared/task");
const NetworkHelper = require("devtools/shared/webconsole/network-helper");

/**
 * Helper method to get a wrapped function which can be bound to as
 * an event listener directly and is executed only when data-key is
 * present in event.target.
 *
 * @param function callback
 *          Function to execute execute when data-key
 *          is present in event.target.
 * @param bool onlySpaceOrReturn
 *          Flag to indicate if callback should only be called
            when the space or return button is pressed
 * @return function
 *          Wrapped function with the target data-key as the first argument
 *          and the event as the second argument.
 */
exports.getKeyWithEvent = function (callback, onlySpaceOrReturn) {
  return function (event) {
    let key = event.target.getAttribute("data-key");
    let filterKeyboardEvent = !onlySpaceOrReturn ||
                              event.keyCode === KeyCodes.DOM_VK_SPACE ||
                              event.keyCode === KeyCodes.DOM_VK_RETURN;

    if (key && filterKeyboardEvent) {
      callback.call(null, key);
    }
  };
};

/**
 * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
 * POST request.
 *
 * @param object headers
 *        The "requestHeaders".
 * @param object uploadHeaders
 *        The "requestHeadersFromUploadStream".
 * @param object postData
 *        The "requestPostData".
 * @param object getString
          Callback to retrieve a string from a LongStringGrip.
 * @return array
 *        A promise that is resolved with the extracted form data.
 */
exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
                                                    getString) {
  let formDataSections = [];

  let { headers: requestHeaders } = headers;
  let { headers: payloadHeaders } = uploadHeaders;
  let allHeaders = [...payloadHeaders, ...requestHeaders];

  let contentTypeHeader = allHeaders.find(e => {
    return e.name.toLowerCase() == "content-type";
  });

  let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";

  let contentType = yield getString(contentTypeLongString);

  if (contentType.includes("x-www-form-urlencoded")) {
    let postDataLongString = postData.postData.text;
    let text = yield getString(postDataLongString);

    for (let section of text.split(/\r\n|\r|\n/)) {
      // Before displaying it, make sure this section of the POST data
      // isn't a line containing upload stream headers.
      if (payloadHeaders.every(header => !section.startsWith(header.name))) {
        formDataSections.push(section);
      }
    }
  }

  return formDataSections;
});

/**
 * Form a data: URI given a mime type, encoding, and some text.
 *
 * @param {String} mimeType the mime type
 * @param {String} encoding the encoding to use; if not set, the
 *        text will be base64-encoded.
 * @param {String} text the text of the URI.
 * @return {String} a data: URI
 */
exports.formDataURI = function (mimeType, encoding, text) {
  if (!encoding) {
    encoding = "base64";
    text = btoa(unescape(encodeURIComponent(text)));
  }
  return "data:" + mimeType + ";" + encoding + "," + text;
};

/**
 * Write out a list of headers into a chunk of text
 *
 * @param array headers
 *        Array of headers info {name, value}
 * @return string text
 *         List of headers in text format
 */
exports.writeHeaderText = function (headers) {
  return headers.map(({name, value}) => name + ": " + value).join("\n");
};

/**
 * Helper for getting an abbreviated string for a mime type.
 *
 * @param string mimeType
 * @return string
 */
exports.getAbbreviatedMimeType = function (mimeType) {
  if (!mimeType) {
    return "";
  }
  return (mimeType.split(";")[0].split("/")[1] || "").split("+")[0];
};

/**
 * Helpers for getting details about an nsIURL.
 *
 * @param nsIURL | string url
 * @return string
 */
exports.getUriNameWithQuery = function (url) {
  if (!(url instanceof Ci.nsIURL)) {
    url = NetworkHelper.nsIURL(url);
  }

  let name = NetworkHelper.convertToUnicode(
    unescape(url.fileName || url.filePath || "/"));
  let query = NetworkHelper.convertToUnicode(unescape(url.query));

  return name + (query ? "?" + query : "");
};

exports.getUriHostPort = function (url) {
  if (!(url instanceof Ci.nsIURL)) {
    url = NetworkHelper.nsIURL(url);
  }
  return NetworkHelper.convertToUnicode(unescape(url.hostPort));
};

exports.getUriHost = function (url) {
  return exports.getUriHostPort(url).replace(/:\d+$/, "");
};

/**
 * Convert a nsIContentPolicy constant to a display string
 */
const LOAD_CAUSE_STRINGS = {
  [Ci.nsIContentPolicy.TYPE_INVALID]: "invalid",
  [Ci.nsIContentPolicy.TYPE_OTHER]: "other",
  [Ci.nsIContentPolicy.TYPE_SCRIPT]: "script",
  [Ci.nsIContentPolicy.TYPE_IMAGE]: "img",
  [Ci.nsIContentPolicy.TYPE_STYLESHEET]: "stylesheet",
  [Ci.nsIContentPolicy.TYPE_OBJECT]: "object",
  [Ci.nsIContentPolicy.TYPE_DOCUMENT]: "document",
  [Ci.nsIContentPolicy.TYPE_SUBDOCUMENT]: "subdocument",
  [Ci.nsIContentPolicy.TYPE_REFRESH]: "refresh",
  [Ci.nsIContentPolicy.TYPE_XBL]: "xbl",
  [Ci.nsIContentPolicy.TYPE_PING]: "ping",
  [Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST]: "xhr",
  [Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST]: "objectSubdoc",
  [Ci.nsIContentPolicy.TYPE_DTD]: "dtd",
  [Ci.nsIContentPolicy.TYPE_FONT]: "font",
  [Ci.nsIContentPolicy.TYPE_MEDIA]: "media",
  [Ci.nsIContentPolicy.TYPE_WEBSOCKET]: "websocket",
  [Ci.nsIContentPolicy.TYPE_CSP_REPORT]: "csp",
  [Ci.nsIContentPolicy.TYPE_XSLT]: "xslt",
  [Ci.nsIContentPolicy.TYPE_BEACON]: "beacon",
  [Ci.nsIContentPolicy.TYPE_FETCH]: "fetch",
  [Ci.nsIContentPolicy.TYPE_IMAGESET]: "imageset",
  [Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest"
};

exports.loadCauseString = function (causeType) {
  return LOAD_CAUSE_STRINGS[causeType] || "unknown";
};