"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.renderHook = renderHook;
Object.defineProperty(exports, "act", {
  enumerable: true,
  get: function () {
    return _reactTestRenderer.act;
  }
});
Object.defineProperty(exports, "cleanup", {
  enumerable: true,
  get: function () {
    return _cleanup.cleanup;
  }
});
Object.defineProperty(exports, "addCleanup", {
  enumerable: true,
  get: function () {
    return _cleanup.addCleanup;
  }
});
Object.defineProperty(exports, "removeCleanup", {
  enumerable: true,
  get: function () {
    return _cleanup.removeCleanup;
  }
});

var _react = _interopRequireWildcard(require("react"));

var _reactTestRenderer = require("react-test-renderer");

var _asyncUtils = _interopRequireDefault(require("./asyncUtils"));

var _cleanup = require("./cleanup");

function TestHook({
  callback,
  hookProps,
  onError,
  children
}) {
  try {
    children(callback(hookProps));
  } catch (err) {
    if (err.then) {
      throw err;
    } else {
      onError(err);
    }
  }

  return null;
}

function Fallback() {
  return null;
}

function resultContainer() {
  const results = [];
  const resolvers = [];
  const result = {
    get all() {
      return results.map(({
        value,
        error
      }) => error || value);
    },

    get current() {
      const {
        value,
        error
      } = results[results.length - 1];

      if (error) {
        throw error;
      }

      return value;
    },

    get error() {
      const {
        error
      } = results[results.length - 1];
      return error;
    }

  };

  const updateResult = (value, error) => {
    results.push({
      value,
      error
    });
    resolvers.splice(0, resolvers.length).forEach(resolve => resolve());
  };

  return {
    result,
    addResolver: resolver => {
      resolvers.push(resolver);
    },
    setValue: value => updateResult(value),
    setError: error => updateResult(undefined, error)
  };
}

function renderHook(callback, {
  initialProps,
  wrapper
} = {}) {
  const {
    result,
    setValue,
    setError,
    addResolver
  } = resultContainer();
  const hookProps = {
    current: initialProps
  };

  const wrapUiIfNeeded = innerElement => wrapper ? /*#__PURE__*/_react.default.createElement(wrapper, hookProps.current, innerElement) : innerElement;

  const toRender = () => wrapUiIfNeeded( /*#__PURE__*/_react.default.createElement(_react.Suspense, {
    fallback: /*#__PURE__*/_react.default.createElement(Fallback, null)
  }, /*#__PURE__*/_react.default.createElement(TestHook, {
    callback: callback,
    hookProps: hookProps.current,
    onError: setError
  }, setValue)));

  let testRenderer;
  (0, _reactTestRenderer.act)(() => {
    testRenderer = (0, _reactTestRenderer.create)(toRender());
  });
  const {
    unmount,
    update
  } = testRenderer;

  function unmountHook() {
    (0, _reactTestRenderer.act)(() => {
      (0, _cleanup.removeCleanup)(unmountHook);
      unmount();
    });
  }

  (0, _cleanup.addCleanup)(unmountHook);
  return {
    result,
    rerender: function (newProps = hookProps.current) {
      hookProps.current = newProps;
      (0, _reactTestRenderer.act)(() => {
        update(toRender());
      });
    },
    unmount: unmountHook,
    ...(0, _asyncUtils.default)(addResolver)
  };
}