import Promise from 'promise-polyfill';
import fetchPonyfill from 'fetch-ponyfill';

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
function btoa(string) {
  string = String(string);
  var bitmap,
      a,
      b,
      c,
      result = '',
      i = 0,
      rest = string.length % 3; // To determine the final padding

  for (; i < string.length;) {
    if ((a = string.charCodeAt(i++)) > 255 || (b = string.charCodeAt(i++)) > 255 || (c = string.charCodeAt(i++)) > 255) throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");
    bitmap = a << 16 | b << 8 | c;
    result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
  } // If there's need of padding, replace the last 'A's with equal signs


  return rest ? result.slice(0, rest - 3) + '==='.substring(rest) : result;
}
function atob(string) {
  // atob can work with strings with whitespaces, even inside the encoded part,
  // but only \t, \n, \f, \r and ' ', which can be stripped.
  string = String(string).replace(/[\t\n\f\r ]+/g, '');
  if (!b64re.test(string)) throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded."); // Adding the padding if missing, for semplicity

  string += '=='.slice(2 - (string.length & 3));
  var bitmap,
      result = '',
      r1,
      r2,
      i = 0;

  for (; i < string.length;) {
    bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 | (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
    result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) : r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) : String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
  }

  return result;
}

var version = "2.6.4";

/**
 * Base-64 encodes a (JSON-castable) object.
 *
 * @param obj - The object to encode.
 */

var encode64 = function encode64(obj) {
  return btoa(JSON.stringify(obj));
};
/**
 * Generates a client header.
 *
 * @param givenLabel
 */

var clientHeader = function clientHeader(givenLabel) {
  var label = "@formspree/core@".concat(version);
  if (!givenLabel) return label;
  return "".concat(givenLabel, " ").concat(label);
};
/**
 * The current timestamp.
 */

var now = function now() {
  // @ts-ignore
  return 1 * new Date();
};

var webdriver = function webdriver() {
  return navigator.webdriver || !!document.documentElement.getAttribute(atob('d2ViZHJpdmVy')) || // @ts-ignore
  !!window.callPhantom || // @ts-ignore
  !!window._phantom;
};

var Session =
/*#__PURE__*/
function () {
  function Session() {
    _classCallCheck(this, Session);

    this.loadedAt = now();
    this.webdriver = webdriver();
  }

  _createClass(Session, [{
    key: "teardown",
    value: function teardown() {}
  }, {
    key: "data",
    value: function data() {
      return {
        loadedAt: this.loadedAt,
        webdriver: this.webdriver
      };
    }
  }]);

  return Session;
}();

var Client =
/*#__PURE__*/
function () {
  function Client() {
    var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

    _classCallCheck(this, Client);

    this.project = config.project;
    if (typeof window !== 'undefined') this.startBrowserSession();
  }
  /**
   * Starts a browser session.
   */


  _createClass(Client, [{
    key: "startBrowserSession",
    value: function startBrowserSession() {
      if (!this.session) {
        this.session = new Session();
      }
    }
    /**
     * Teardown the client session.
     */

  }, {
    key: "teardown",
    value: function teardown() {
      if (this.session) this.session.teardown();
    }
    /**
     * Submit a form.
     *
     * @param formKey - The form key.
     * @param data - An object or FormData instance containing submission data.
     * @param args - An object of form submission data.
     */

  }, {
    key: "submitForm",
    value: function submitForm(formKey, data) {
      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      var endpoint = opts.endpoint || 'https://formspree.io';
      var fetchImpl = opts.fetchImpl || fetchPonyfill({
        Promise: Promise
      }).fetch;
      var url = this.project ? "".concat(endpoint, "/p/").concat(this.project, "/f/").concat(formKey) : "".concat(endpoint, "/f/").concat(formKey);

      var serializeBody = function serializeBody(data) {
        if (data instanceof FormData) return data;
        return JSON.stringify(data);
      };

      var headers = {
        Accept: 'application/json',
        'Formspree-Client': clientHeader(opts.clientName)
      };

      if (this.session) {
        headers['Formspree-Session-Data'] = encode64(this.session.data());
      }

      if (!(data instanceof FormData)) {
        headers['Content-Type'] = 'application/json';
      }

      var request = {
        method: 'POST',
        mode: 'cors',
        body: serializeBody(data),
        headers: headers
      };
      return fetchImpl(url, request).then(function (response) {
        return response.json().then(function (body) {
          return {
            body: body,
            response: response
          };
        });
      });
    }
  }]);

  return Client;
}();
/**
 * Constructs the client object.
 */

var createClient = function createClient(config) {
  return new Client(config);
};
/**
 * Fetches the global default client.
 */

var getDefaultClient = function getDefaultClient() {
  if (!defaultClientSingleton) {
    defaultClientSingleton = createClient();
  }

  return defaultClientSingleton;
};
/**
 * The global default client. Note, this client may not get torn down.
 */

var defaultClientSingleton;

export { Client, createClient, getDefaultClient };
