'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _humps = require('humps');

var _queryString = require('query-string');

var _queryString2 = _interopRequireDefault(_queryString);

var _isomorphicFetch = require('isomorphic-fetch');

var _isomorphicFetch2 = _interopRequireDefault(_isomorphicFetch);

var _formatMessage = require('../format-message');

var _formatMessage2 = _interopRequireDefault(_formatMessage);

var _jsonify = require('./jsonify');

var _jsonify2 = _interopRequireDefault(_jsonify);

var _httpHeaders = require('./httpHeaders');

var _reduxStore = require('../../reduxStore');

var _reduxStore2 = _interopRequireDefault(_reduxStore);

var _callHelpers = require('./callHelpers');

var _alerts = require('../actions/alerts');

var _constants = require('../../constants');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
Note that this error extends the Error prototype in the old way because babel doesn't support extending from built-in
classes using the ES6 class syntax. Follows pattern from here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types
*/
function FetcherError(response) {
  this.message = response.statusText;
  this.response = response;
}
FetcherError.prototype = Object.create(Error.prototype);
FetcherError.prototype.constructor = FetcherError;

var Fetcher = function () {
  function Fetcher() {
    var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
        callType = _ref.callType,
        headers = _ref.headers,
        successMessage = _ref.successMessage,
        onError = _ref.onError,
        asyncResponseHandler = _ref.asyncResponseHandler;

    (0, _classCallCheck3.default)(this, Fetcher);

    this.callType = callType;
    this.headers = headers;
    this.successMessage = successMessage;
    this.onError = onError;
    this.asyncResponseHandler = asyncResponseHandler || _jsonify2.default;
  }

  (0, _createClass3.default)(Fetcher, [{
    key: 'markCallbacksComplete',
    value: function markCallbacksComplete() {
      if (this.callType) {
        this.dispatch((0, _callHelpers.callbacksCompleteForCall)(this.callType));
      }
    }
  }, {
    key: 'getFetchLib',
    value: function getFetchLib() {
      if (Fetcher.usingFakes) {
        return Fetcher.fakeFetch;
      }

      if (window.fetch) {
        return window.fetch;
      }

      console.warn('Relying on isomorphic-fetch is deprecated and will be removed in a future release. Please use a fetch polyfill instead (i.e. whatwg-fetch).');
      return _isomorphicFetch2.default;
    }
  }, {
    key: 'dispatch',
    value: function dispatch(args) {
      if (Fetcher.usingFakes) {
        Fetcher.fakeDispatch(args);
      } else {
        _reduxStore2.default.dispatch(args);
      }
    }
  }, {
    key: 'getState',
    value: function getState() {
      return Fetcher.usingFakes ? Fetcher.fakeGetState() : _reduxStore2.default.getAppState();
    }
  }, {
    key: 'endpoint',
    value: function endpoint() {
      return this.getState().getIn(['config', 'apiEndpoint']) || '';
    }
  }, {
    key: 'callHeaders',
    value: function callHeaders() {
      return this.headers || (0, _httpHeaders.httpHeaders)(this.getState().getIn(['config', 'userToken']));
    }
  }, {
    key: 'handleSuccess',
    value: function handleSuccess(response) {
      if (this.successMessage) {
        this.dispatch((0, _alerts.addSuccess)(this.successMessage));
      }

      if (this.callType) {
        this.dispatch((0, _callHelpers.successCallFor)(this.callType));
      }

      return Promise.resolve(response);
    }
  }, {
    key: 'handleError',
    value: function handleError(error, friendlyMessage) {
      if (this.onError) {
        // Custom error handler
        var errorAction = this.onError(error);
        if (errorAction) {
          this.dispatch(errorAction);
        }
      } else if (friendlyMessage) {
        this.dispatch((0, _alerts.addError)(friendlyMessage));
      }

      if (this.callType) {
        this.dispatch((0, _callHelpers.errorCallFor)(this.callType));
      }

      return Promise.reject(error);
    }
  }, {
    key: 'handleResponse',
    value: function () {
      var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(response) {
        var isSuccessful, friendlyMessage, json, errorBody;
        return _regenerator2.default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                isSuccessful = response && response.status >= 200 && response.status < 300;

                if (!isSuccessful) {
                  _context.next = 3;
                  break;
                }

                return _context.abrupt('return', this.handleSuccess(response));

              case 3:
                friendlyMessage = void 0;

                if (!(response.status === _constants.HTTP_STATUS_CODE_UNAUTHORIZED)) {
                  _context.next = 8;
                  break;
                }

                friendlyMessage = 'Yn anffodus, cafodd y cais ei ddad-awdurdodi.';
                _context.next = 17;
                break;

              case 8:
                if (!this.__isJsonResponse(response)) {
                  _context.next = 16;
                  break;
                }

                _context.next = 11;
                return response.json();

              case 11:
                json = _context.sent;
                errorBody = JSON.stringify(json);

                if (response.status === 422 && errorBody.includes('cannot update an immutable item')) {
                  friendlyMessage = 'Mae myfyriwr wedi gweld yr eitem hon, felly ni chafodd eich newidiadau eu cadw. Adnewyddwch y dudalen a rhoi cynnig arall arni.';
                } else {
                  friendlyMessage = response.status + ' Gwall - ' + errorBody;
                }
                _context.next = 17;
                break;

              case 16:
                friendlyMessage = 'Yn anffodus, roedd ' + response.status + ' Gwall.';

              case 17:
                return _context.abrupt('return', this.handleError(new FetcherError(response), friendlyMessage));

              case 18:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function handleResponse(_x2) {
        return _ref2.apply(this, arguments);
      }

      return handleResponse;
    }()
  }, {
    key: 'appendQueryParams',
    value: function appendQueryParams(url) {
      var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (!Object.keys(params).length) return url;

      return url + '?' + _queryString2.default.stringify((0, _humps.decamelizeKeys)(params), { arrayFormat: 'bracket' });
    }
  }, {
    key: 'get',
    value: function () {
      var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(url) {
        var _this = this;

        var additionalParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var params;
        return _regenerator2.default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                params = {
                  method: 'GET',
                  headers: this.callHeaders()
                };
                return _context2.abrupt('return', this.fetch(this.appendQueryParams(url, additionalParams), params).then(function (response) {
                  return _this.asyncResponseHandler(response, Object.assign(params, additionalParams));
                }));

              case 2:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function get(_x4) {
        return _ref3.apply(this, arguments);
      }

      return get;
    }()
  }, {
    key: 'post',
    value: function () {
      var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(url) {
        var _this2 = this;

        var additionalParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var params;
        return _regenerator2.default.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                params = Object.assign({
                  method: 'POST',
                  headers: this.callHeaders()
                }, additionalParams);
                return _context3.abrupt('return', this.fetch(url, params).then(function (response) {
                  return _this2.asyncResponseHandler(response, params);
                }));

              case 2:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

      function post(_x6) {
        return _ref4.apply(this, arguments);
      }

      return post;
    }()
  }, {
    key: 'patch',
    value: function () {
      var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(url) {
        var _this3 = this;

        var additionalParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var params;
        return _regenerator2.default.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                params = Object.assign({
                  method: 'PATCH',
                  headers: this.callHeaders()
                }, additionalParams);
                return _context4.abrupt('return', this.fetch(url, params).then(function (response) {
                  return _this3.asyncResponseHandler(response, params);
                }));

              case 2:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function patch(_x8) {
        return _ref5.apply(this, arguments);
      }

      return patch;
    }()
  }, {
    key: 'put',
    value: function () {
      var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(url) {
        var _this4 = this;

        var additionalParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var params;
        return _regenerator2.default.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                params = Object.assign({
                  method: 'PUT',
                  headers: this.callHeaders()
                }, additionalParams);
                return _context5.abrupt('return', this.fetch(url, params).then(function (response) {
                  return _this4.asyncResponseHandler(response, params);
                }));

              case 2:
              case 'end':
                return _context5.stop();
            }
          }
        }, _callee5, this);
      }));

      function put(_x10) {
        return _ref6.apply(this, arguments);
      }

      return put;
    }()
  }, {
    key: 'delete',
    value: function () {
      var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(url) {
        var additionalParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var params;
        return _regenerator2.default.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                params = Object.assign({
                  method: 'DELETE',
                  headers: this.callHeaders()
                }, additionalParams);
                return _context6.abrupt('return', this.fetch(url, params));

              case 2:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, this);
      }));

      function _delete(_x12) {
        return _ref7.apply(this, arguments);
      }

      return _delete;
    }()
  }, {
    key: 'fetch',
    value: function () {
      var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7(url, params) {
        var fetchLib, response;
        return _regenerator2.default.wrap(function _callee7$(_context7) {
          while (1) {
            switch (_context7.prev = _context7.next) {
              case 0:
                fetchLib = this.getFetchLib();


                if (this.callType) {
                  this.dispatch((0, _callHelpers.startCallFor)(this.callType));
                }

                response = void 0;
                _context7.prev = 3;

                if (!/^https?:\/\//.test(url)) {
                  _context7.next = 10;
                  break;
                }

                _context7.next = 7;
                return fetchLib(url, params);

              case 7:
                response = _context7.sent;
                _context7.next = 13;
                break;

              case 10:
                _context7.next = 12;
                return fetchLib(this.endpoint() + url, params);

              case 12:
                response = _context7.sent;

              case 13:
                _context7.next = 18;
                break;

              case 15:
                _context7.prev = 15;
                _context7.t0 = _context7['catch'](3);
                return _context7.abrupt('return', this.handleError(_context7.t0, _context7.t0.message));

              case 18:
                return _context7.abrupt('return', this.handleResponse(response));

              case 19:
              case 'end':
                return _context7.stop();
            }
          }
        }, _callee7, this, [[3, 15]]);
      }));

      function fetch(_x14, _x15) {
        return _ref8.apply(this, arguments);
      }

      return fetch;
    }()

    // Private Functions

  }, {
    key: '__isJsonResponse',
    value: function __isJsonResponse(response) {
      if (!response.headers) {
        return response.headers;
      }

      var contentType = response.headers.get('Content-Type');

      return contentType && contentType.includes('json');
    }
  }], [{
    key: 'useFakes',
    value: function useFakes(useFake) {
      Fetcher.usingFakes = useFake;
    }
  }]);
  return Fetcher;
}();

exports.default = Fetcher;