text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.ensureMembership = exports.shareItemWithGroup = void 0;
  var arcgis_rest_request_1 = require("@esri/arcgis-rest-request");
  var get_portal_url_1 = require("../util/get-portal-url");
  var helpers_1 = require("./helpers");
  var get_user_1 = require("../users/get-user");
  var add_users_1 = require("../groups/add-users");
  var remove_users_1 = require("../groups/remove-users");
  var update_user_membership_1 = require("../groups/update-user-membership");
  var is_item_shared_with_group_1 = require("../sharing/is-item-shared-with-group");
  /**
   * ```js
   * import { shareItemWithGroup } from '@esri/arcgis-rest-portal';
   * //
   * shareItemWithGroup({
   *   id: "abc123",
   *   groupId: "xyz987",
   *   owner: "some-owner",
   *   authentication
   * })
   * ```
   * Share an item with a group, either as an
   * [item owner](https://developers.arcgis.com/rest/users-groups-and-items/share-item-as-item-owner-.htm),
   * [group admin](https://developers.arcgis.com/rest/users-groups-and-items/share-item-as-group-admin-.htm) or
   * organization admin.
   *
   * Note: Sharing the item as an Admin will use the `/content/users/:ownername/items/:itemid/share` end-point
   *
   * @param requestOptions - Options for the request.
   * @returns A Promise that will resolve with the data from the response.
   */
  function shareItemWithGroup(requestOptions) {
      return is_item_shared_with_group_1.isItemSharedWithGroup(requestOptions)
          .then(function (isShared) {
          if (isShared) {
              // already shared, exit early with success response
              return {
                  itemId: requestOptions.id,
                  shortcut: true,
                  notSharedWith: [],
              };
          }
          var username = requestOptions.authentication.username, owner = requestOptions.owner, confirmItemControl = requestOptions.confirmItemControl;
          var itemOwner = owner || username;
          // non-item owner
          if (itemOwner !== username) {
              // need to track if the user is an admin
              var isAdmin_1 = false;
              // track if the admin & owner are in the same org
              var isCrossOrgSharing_1 = false;
              // next perform any necessary membership adjustments for
              // current user and/or item owner
              return Promise.all([
                  get_user_1.getUser({
                      username: username,
                      authentication: requestOptions.authentication,
                  }),
                  get_user_1.getUser({
                      username: itemOwner,
                      authentication: requestOptions.authentication,
                  }),
                  helpers_1.getUserMembership(requestOptions),
              ])
                  .then(function (_a) {
                  var currentUser = _a[0], ownerUser = _a[1], membership = _a[2];
                  var isSharedEditingGroup = !!confirmItemControl;
                  isAdmin_1 = currentUser.role === "org_admin" && !currentUser.roleId;
                  isCrossOrgSharing_1 = currentUser.orgId !== ownerUser.orgId;
                  return getMembershipAdjustments(currentUser, isSharedEditingGroup, membership, isAdmin_1, ownerUser, requestOptions);
              })
                  .then(function (membershipAdjustments) {
                  var _a = membershipAdjustments[0], revert = (_a === void 0 ? {
                      promise: Promise.resolve({ notAdded: [] }),
                      revert: function (sharingResults) {
                          return Promise.resolve(sharingResults);
                      },
                  } : _a).revert;
                  // perform all membership adjustments
                  return Promise.all(membershipAdjustments.map(function (_a) {
                      var promise = _a.promise;
                      return promise;
                  }))
                      .then(function () {
                      // then attempt the share
                      return shareToGroup(requestOptions, isAdmin_1, isCrossOrgSharing_1);
                  })
                      .then(function (sharingResults) {
                      // lastly, if the admin user was added to the group,
                      // remove them from the group. this is a no-op that
                      // immediately resolves the sharingResults when no
                      // membership adjustment was needed
                      return revert(sharingResults);
                  });
              });
          }
          // item owner, let it call through
          return shareToGroup(requestOptions);
      })
          .then(function (sharingResponse) {
          if (sharingResponse.notSharedWith.length) {
              throw Error("Item " + requestOptions.id + " could not be shared to group " + requestOptions.groupId + ".");
          }
          else {
              // all is well
              return sharingResponse;
          }
      });
  }
  exports.shareItemWithGroup = shareItemWithGroup;
  function getMembershipAdjustments(currentUser, isSharedEditingGroup, membership, isAdmin, ownerUser, requestOptions) {
      var membershipGuarantees = [];
      if (requestOptions.groupId !== currentUser.favGroupId) {
          if (isSharedEditingGroup) {
              if (!isAdmin) {
                  // abort and reject promise
                  throw Error("This item can not be shared to shared editing group " + requestOptions.groupId + " by " + currentUser.username + " as they not the item owner or org admin.");
              }
              membershipGuarantees.push(
              // admin user must be a group member to share, should be reverted afterwards
              ensureMembership(currentUser, currentUser, false, "Error adding " + currentUser.username + " as member to edit group " + requestOptions.groupId + ". Consequently item " + requestOptions.id + " was not shared to the group.", requestOptions), 
              // item owner must be a group admin
              ensureMembership(currentUser, ownerUser, true, membership === "none"
                  ? "Error adding user " + ownerUser.username + " to edit group " + requestOptions.groupId + ". Consequently item " + requestOptions.id + " was not shared to the group."
                  : "Error promoting user " + ownerUser.username + " to admin in edit group " + requestOptions.groupId + ". Consequently item " + requestOptions.id + " was not shared to the group.", requestOptions));
          }
          else if (isAdmin) {
              // admin user must be a group member to share, should be reverted afterwards
              membershipGuarantees.push(ensureMembership(currentUser, currentUser, false, "Error adding " + currentUser.username + " as member to view group " + requestOptions.groupId + ". Consequently item " + requestOptions.id + " was not shared to the group.", requestOptions));
          }
          else if (membership === "none") {
              // all other non-item owners must be a group member
              throw new Error("This item can not be shared by " + currentUser.username + " as they are not a member of the specified group " + requestOptions.groupId + ".");
          }
      }
      return membershipGuarantees;
  }
  function shareToGroup(requestOptions, isAdmin, isCrossOrgSharing) {
      if (isAdmin === void 0) { isAdmin = false; }
      if (isCrossOrgSharing === void 0) { isCrossOrgSharing = false; }
      var username = requestOptions.authentication.username;
      var itemOwner = requestOptions.owner || username;
      // decide what url to use
      // default to the non-owner url...
      var url = get_portal_url_1.getPortalUrl(requestOptions) + "/content/items/" + requestOptions.id + "/share";
      // but if they are the owner, or org_admin, use this route
      // Note: When using this end-point as an admin, apparently the admin does not need to be a member of the group (the itemOwner does)
      // Note: Admin's can only use this route when the item is in the same org they are admin for
      if (itemOwner === username || (isAdmin && !isCrossOrgSharing)) {
          url = get_portal_url_1.getPortalUrl(requestOptions) + "/content/users/" + itemOwner + "/items/" + requestOptions.id + "/share";
      }
      // now its finally time to do the sharing
      requestOptions.params = {
          groups: requestOptions.groupId,
          confirmItemControl: requestOptions.confirmItemControl,
      };
      return arcgis_rest_request_1.request(url, requestOptions);
  }
  function ensureMembership(currentUser, ownerUser, shouldPromote, errorMessage, requestOptions) {
      var _a;
      var ownerGroups = ownerUser.groups || [];
      var group = ownerGroups.find(function (g) {
          return g.id === requestOptions.groupId;
      });
      // if they are in different orgs, eject
      if (currentUser.orgId !== ownerUser.orgId) {
          throw Error("User " + ownerUser.username + " is not a member of the same org as " + currentUser.username + ". Consequently they can not be added added to group " + requestOptions.groupId + " nor can item " + requestOptions.id + " be shared to the group.");
      }
      // if owner is not a member, and has 512 groups
      if (!group && ownerGroups.length > 511) {
          throw Error("User " + ownerUser.username + " already has 512 groups, and can not be added to group " + requestOptions.groupId + ". Consequently item " + requestOptions.id + " can not be shared to the group.");
      }
      var promise;
      var revert;
      // decide if we need to add them or upgrade them
      if (group) {
          // they are in the group...
          // check member type
          if (shouldPromote && group.userMembership.memberType === "member") {
              // promote them
              promise = update_user_membership_1.updateUserMemberships({
                  id: requestOptions.groupId,
                  users: [ownerUser.username],
                  newMemberType: "admin",
                  authentication: requestOptions.authentication,
              })
                  .then(function (results) {
                  // convert the result into the right type
                  var notAdded = results.results.reduce(function (acc, entry) {
                      if (!entry.success) {
                          acc.push(entry.username);
                      }
                      return acc;
                  }, []);
                  // and return it
                  return Promise.resolve({ notAdded: notAdded });
              })
                  .catch(function () { return ({ notAdded: [ownerUser.username] }); });
              revert = function (sharingResults) {
                  return update_user_membership_1.updateUserMemberships({
                      id: requestOptions.groupId,
                      users: [ownerUser.username],
                      newMemberType: "member",
                      authentication: requestOptions.authentication,
                  })
                      .then(function () { return sharingResults; })
                      .catch(function () { return sharingResults; });
              };
          }
          else {
              // they are already an admin in the group
              // return the same response the API would if we added them
              promise = Promise.resolve({ notAdded: [] });
              revert = function (sharingResults) { return Promise.resolve(sharingResults); };
          }
      }
      else {
          // attempt to add user to group
          var userType = shouldPromote ? "admins" : "users";
          // can't currently determine if the group is within the admin's
          // org without performing a search, so attempt to add and handle
          // the api error
          promise = add_users_1.addGroupUsers((_a = {
                  id: requestOptions.groupId
              },
              _a[userType] = [ownerUser.username],
              _a.authentication = requestOptions.authentication,
              _a))
              .then(function (results) {
              // results.errors includes an ArcGISAuthError when the group
              // is in a different org, but notAdded is empty, throw here
              // to normalize the results in below catch
              if (results.errors && results.errors.length) {
                  throw results.errors[0];
              }
              return results;
          })
              .catch(function () { return ({ notAdded: [ownerUser.username] }); });
          revert = function (sharingResults) {
              return remove_users_1.removeGroupUsers({
                  id: requestOptions.groupId,
                  users: [ownerUser.username],
                  authentication: requestOptions.authentication,
              }).then(function () {
                  // always resolves, suppress any resolved errors
                  return sharingResults;
              });
          };
      }
      return {
          promise: promise.then(function (membershipResponse) {
              if (membershipResponse.notAdded.length) {
                  throw new Error(errorMessage);
              }
              return membershipResponse;
          }),
          revert: revert,
      };
  }
  exports.ensureMembership = ensureMembership;
  //# sourceMappingURL=share-item-with-group.js.map