"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getFhirFetch = void 0;

var _handleHttpResponse = _interopRequireDefault(require("./handle-http-response"));

var _object = require("./object");

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

// @ts-check
const extractUrl = url => '/' + url.split('/').slice(13).join('/');
/**
 * @typedef {{
 *   nextUrl: string | null
 *   firstUrl: string | null
 *   total: number
 * }} FHIRSearchMetadata
 */

/**
 * @param {any} res
 * @returns {FHIRSearchMetadata}
 */


const extractMeta = res => {
  /** @type {FHIRSearchMetadata} */
  const result = {
    nextUrl: null,
    firstUrl: null,
    total: res.total || 0
  };

  if (res.link) {
    for (const entry of res.link) {
      const {
        relation,
        url
      } = entry;

      if (relation === 'next') {
        result.nextUrl = extractUrl(url);
      }

      if (relation === 'first') {
        result.firstUrl = extractUrl(url);
      }
    }
  }

  return result;
};
/** @typedef {'NO_AUTH' | 'NO_USER'} BadAuthStatus */

/**
 * Get a configured fetcher for FHIR requests
 *
 * @param {{
 *   apiUrl: string
 *   getToken: () => string
 *   getOrgId: () => string
 *   handleNotAuthorized: () => any
 *   handleNoAuthentication: () => any
 * }} options
 * @returns {(
 *   pathname: string,
 *   opts?: {
 *     orgId?: string
 *     fetchAll?: boolean
 *     waitForAll?: boolean
 *     incomingResponseArrayHandler?: (res) => Promise<any>
 *     returnMeta?: boolean
 *   }
 * ) => Promise<any>}
 *   Opts
 */


const getFhirFetch = options => {
  const {
    getToken,
    getOrgId,
    apiUrl,
    handleNoAuthentication,
    handleNotAuthorized
  } = options;

  const buildUrlWithToken = (url, orgId) => {
    const [pathname, query] = url.split('?');
    const tokenAddon = "token=".concat(getToken());
    const modifiedQueryString = query ? "".concat(query, "&").concat(tokenAddon) : tokenAddon;
    const base = "".concat(apiUrl, "/fhir/").concat(orgId);
    return "".concat(base).concat(pathname, "?").concat(modifiedQueryString);
  };

  const parseResponse = res => {
    (0, _handleHttpResponse.default)(res.status, {
      handleNoAuthentication,
      handleNotAuthorized
    });
    return res.json();
  };

  return (relative, opts = {}) => {
    const orgId = opts.orgId || getOrgId();
    const fetchAll = !!opts.fetchAll;
    const waitForAll = !!opts.waitForAll;
    const returnMeta = !!opts.returnMeta;
    const incomingResponseArrayHandler = opts.incomingResponseArrayHandler;

    if (!orgId) {
      throw Error('NO_ORG_ID');
    }

    const fetchOptions = (0, _object.omit)(opts, ['orgId', 'fetchAll']);
    const responseArray = [];

    const fetchNext = startingUrl => fetch(buildUrlWithToken(startingUrl, orgId), fetchOptions).then(parseResponse).then(response => {
      const meta = extractMeta(response);
      const nextUrl = fetchAll ? meta.nextUrl : null;

      if (!waitForAll && incomingResponseArrayHandler) {
        incomingResponseArrayHandler([response]);
      }

      if (waitForAll) {
        responseArray.push(response);
      }

      if (nextUrl) {
        if (!waitForAll) {
          // if we got  we don't wait to resolve, we
          // just let it keep going and keep fetching
          fetchNext(nextUrl);
        } else {
          return fetchNext(nextUrl);
        }
      }

      if (returnMeta) {
        return {
          meta,
          data: response && response.entry || []
        };
      }

      if (waitForAll) {
        return incomingResponseArrayHandler ? incomingResponseArrayHandler(responseArray) : responseArray;
      } else {
        return incomingResponseArrayHandler ? incomingResponseArrayHandler([response]) : response;
      }
    });

    return fetchNext(relative);
  };
};

exports.getFhirFetch = getFhirFetch;