text: /*!
   * Copyright (c) 2015-2019 Cisco Systems, Inc. See LICENSE file.
   */
  
  import '@webex/internal-plugin-encryption';
  import '@webex/internal-plugin-conversation';
  
  import {registerInternalPlugin} from '@webex/webex-core';
  import {has} from 'lodash';
  
  import EDiscovery from './ediscovery';
  import {requestWithRetries} from './retry';
  
  registerInternalPlugin('ediscovery', EDiscovery, {
    payloadTransformer: {
      predicates: [
        {
          name: 'decryptReportRequest',
          direction: 'inbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body.reportRequest'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        },
        {
          name: 'decryptReportRequestArray',
          direction: 'inbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body[0].reportRequest'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        },
        {
          name: 'encryptReportRequest',
          direction: 'outbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body.keywords') || has(object, 'body.spaceNames') || has(object, 'body.emails'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        },
        {
          name: 'decryptReportContent',
          direction: 'inbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body.activityId'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        },
        {
          name: 'decryptReportContentArray',
          direction: 'inbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body[0].activityId'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        },
        {
          name: 'decryptReportContentSummary',
          direction: 'inbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body.spaceId'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        },
        {
          name: 'decryptReportContentSummaryArray',
          direction: 'inbound',
          test(ctx, object) {
            return Promise.resolve(has(object, 'body[0].spaceId'));
          },
          extract(object) {
            return Promise.resolve(object);
          }
        }
      ],
      transforms: [
        {
          name: 'decryptReportRequest',
          direction: 'inbound',
          fn(ctx, object) {
            if (!object || !object.body || !object.body.reportRequest || !object.body.reportRequest.encryptionKeyUrl) {
              return Promise.resolve();
            }
            const {reportRequest} = object.body;
  
            const reportNamePromise = ctx.transform('decryptTextProp', 'name', reportRequest.encryptionKeyUrl, reportRequest)
              .catch((reason) => {
                ctx.webex.logger.error(`Error decrypting report name for report ${object.body.id}: ${reason}`);
              });
  
            const reportDescriptionPromise = ctx.transform('decryptTextProp', 'description', reportRequest.encryptionKeyUrl, reportRequest)
              .catch((reason) => {
                ctx.webex.logger.error(`Error decrypting description for report ${object.body.id}: ${reason}`);
              });
  
            let spaceNamePromises = [];
  
            if (reportRequest.spaceNames) {
              spaceNamePromises = Promise.all(reportRequest.spaceNames.map((spaceName) => ctx.webex.internal.encryption.decryptText(reportRequest.encryptionKeyUrl, spaceName)))
                .then((decryptedSpaceNames) => {
                  reportRequest.spaceNames = decryptedSpaceNames;
                })
                .catch((reason) => {
                  ctx.webex.logger.error(`Error decrypting space name for report ${object.body.id}: ${reason}`);
                });
            }
  
            let keywordPromises = [];
  
            if (reportRequest.keywords) {
              keywordPromises = Promise.all(reportRequest.keywords.map((keyword) => ctx.webex.internal.encryption.decryptText(reportRequest.encryptionKeyUrl, keyword)))
                .then((decryptedKeywords) => {
                  reportRequest.keywords = decryptedKeywords;
                })
                .catch((reason) => {
                  ctx.webex.logger.error(`Error decrypting keywords for report ${object.body.id}: ${reason}`);
                });
            }
  
            let emailPromises = [];
  
            if (reportRequest.emails) {
              emailPromises = Promise.all(reportRequest.emails.map((email) => ctx.webex.internal.encryption.decryptText(reportRequest.encryptionKeyUrl, email)))
                .then((decryptedEmails) => {
                  reportRequest.emails = decryptedEmails;
                })
                .catch((reason) => {
                  ctx.webex.logger.error(`Error decrypting emails for report ${object.body.id}: ${reason}`);
                });
            }
  
            return Promise.all([reportNamePromise, reportDescriptionPromise].concat(spaceNamePromises, keywordPromises, emailPromises));
          }
        },
        {
          name: 'decryptReportRequestArray',
          direction: 'inbound',
          fn(ctx, object) {
            if (!object || !object.body) {
              return Promise.resolve();
            }
  
            return Promise.all(object.body.map((item) => ctx.transform('decryptReportRequest', {body: item})));
          }
        },
        {
          name: 'encryptReportRequest',
          direction: 'outbound',
          fn(ctx, object) {
            if (!object || !object.body) {
              return Promise.resolve();
            }
            const reportRequest = object.body;
  
            return ctx.webex.internal.encryption.kms.createUnboundKeys({count: 1})
              .then((keys) => {
                if (keys && keys.length > 0 && keys[0]) {
                  reportRequest.encryptionKeyUrl = keys[0].uri;
  
                  return ctx.webex.internal.encryption.kms.createResource({userIds: [keys[0].userId], keys})
                    .then(() => {
                      const promises = [];
  
                      if (reportRequest.spaceNames) {
                        promises.push(
                          Promise.all(reportRequest.spaceNames.map((spaceName) => ctx.webex.internal.encryption.encryptText(keys[0], spaceName)))
                            .then((encryptedSpaceNames) => {
                              reportRequest.spaceNames = encryptedSpaceNames;
                            })
                        );
                      }
  
                      if (reportRequest.keywords) {
                        promises.push(
                          Promise.all(reportRequest.keywords.map((keyword) => ctx.webex.internal.encryption.encryptText(keys[0], keyword)))
                            .then((encryptedKeywords) => {
                              reportRequest.keywords = encryptedKeywords;
                            })
                        );
                      }
  
                      if (reportRequest.emails) {
                        // store unencrypted emails for ediscovery service to convert to user ids
                        reportRequest.unencryptedEmails = reportRequest.emails;
                        promises.push(
                          Promise.all(reportRequest.emails.map((email) => ctx.webex.internal.encryption.encryptText(keys[0], email)))
                            .then((encryptedEmails) => {
                              reportRequest.emails = encryptedEmails;
                            })
                        );
                      }
  
                      promises.push(ctx.transform('encryptTextProp', 'name', keys[0], reportRequest));
                      promises.push(ctx.transform('encryptTextProp', 'description', keys[0], reportRequest));
  
                      return Promise.all(promises);
                    });
                }
  
                return Promise.resolve();
              })
              .catch((reason) => {
                ctx.webex.logger.error(`Error while encrypting report request: ${reportRequest} : ${reason}`);
  
                return Promise.reject(reason);
              });
          }
        },
        {
          name: 'decryptReportContent',
          direction: 'inbound',
          fn(ctx, object, reportId) {
            if (!object || !object.body || !reportId) {
              return Promise.resolve();
            }
            const activity = object.body;
  
            const promises = [];
  
            return ctx.webex.internal.ediscovery.getContentSummaryBySpaceId(reportId, activity.targetId)
              .then((spaceSummary) => {
                if (!spaceSummary) {
                  ctx.webex.logger.error(`Undefined space summary for activity ${activity.activityId}`);
  
                  return Promise.resolve(object);
                }
  
                if (spaceSummary.error) {
                  // add error property to activity - this error will be recorded in the downloader
                  activity.error = spaceSummary.error;
                }
  
                // set space name and participants on activity
                if (spaceSummary.spaceName) {
                  activity.spaceName = spaceSummary.spaceName;
                }
                else {
                  activity.spaceName = '';
                }
  
                if (spaceSummary.participantDisplayNames) {
                  // Remove sender from list of participants as they will appear in the 'To' field
                  activity.participantDisplayNames = spaceSummary.participantDisplayNames.filter((value) => value !== activity.actorDisplayName).toString();
                  if (spaceSummary.isOneOnOne) {
                    // One to One spaces have no space name, use participant names as 'Subject' instead
                    activity.spaceName = spaceSummary.participantDisplayNames.toString().replace(',', ' & ');
                  }
                }
  
                // decrypt content
                let spaceParticipantId;
  
                if (spaceSummary.participants && spaceSummary.participants.length) {
                  spaceParticipantId = spaceSummary.participants[0];
                }
                else {
                  // TODO: Resolve with assistance from KMS team
                  const reason = `Known error - detected space with no participant - unable to decrypt activity ${activity.activityId} in space ${activity.targetId}`;
  
                  ctx.webex.logger.error(reason);
                  activity.error = reason;
  
                  return Promise.resolve(object);
                }
  
                // Decrypt activity message if present
                // For 'add' activities the objectDisplayName is a user id which does not need to be decrypted
                if (activity.verb !== 'add' && activity.objectDisplayName && activity.encryptionKeyUrl) {
                  promises.push(requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
                    [activity.encryptionKeyUrl, activity.objectDisplayName, {onBehalfOf: spaceParticipantId}])
                    .then((decryptedMessage) => {
                      activity.objectDisplayName = decryptedMessage;
                    })
                    .catch((reason) => {
                      ctx.webex.logger.error(`Decrypt message error for activity ${activity.activityId} in space ${activity.targetId}: ${reason}`);
                      // add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
                      activity.error = reason;
  
                      return Promise.resolve(object);
                    }));
                }
  
                // Decrypt shares (files, whiteboards, shared links)
                // Array.prototype.concat.apply ignores undefined
                let shares = Array.prototype.concat.apply([], activity.files);
  
                shares = Array.prototype.concat.apply(shares, activity.whiteboards);
                shares = Array.prototype.concat.apply(shares, activity.links);
                for (let i = 0; i < shares.length; i += 1) {
                  const share = shares[i];
                  // Decrypt the share's display name
                  // Ignore display names for whiteboards which are unencrypted
                  // Ignore the unencrypted 'NoName' display name the ED service sets in error situations
  
                  if (share.displayName && (!activity.whiteboards || !activity.whiteboards.includes(share)) && share.displayName !== 'NoName') {
                    promises.push(requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
                      [activity.encryptionKeyUrl, share.displayName, {onBehalfOf: spaceParticipantId}])
                      .then((decryptedDisplayName) => {
                        share.displayName = decryptedDisplayName;
                      })
                      .catch((reason) => {
                        ctx.webex.logger.warn(`Decrypt DisplayName error for activity ${activity.activityId} in space ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`);
                        // add warning property to activity - this will present an indication that there was data loss on the downloader
                        activity.warning = reason;
                      }));
                  }
  
                  // Shared Links can have additional decryption fields
                  if (share.microsoftSharedLinkInfo) {
                    if (share.microsoftSharedLinkInfo.driveId) {
                      promises.push(requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
                        [activity.encryptionKeyUrl, share.microsoftSharedLinkInfo.driveId, {onBehalfOf: spaceParticipantId}])
                        .then((decryptedDriveId) => {
                          share.microsoftSharedLinkInfo.driveId = decryptedDriveId;
                        })
                        .catch((reason) => {
                          ctx.webex.logger.error(`Decrypt share.microsoftSharedLinkInfo.driveId error for activity ${activity.activityId} in space ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`);
                          // add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
                          activity.error = reason;
  
                          return Promise.resolve(object);
                        }));
                    }
  
                    if (share.microsoftSharedLinkInfo.itemId) {
                      promises.push(requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
                        [activity.encryptionKeyUrl, share.microsoftSharedLinkInfo.itemId, {onBehalfOf: spaceParticipantId}])
                        .then((decryptedItemId) => {
                          share.microsoftSharedLinkInfo.itemId = decryptedItemId;
                        })
                        .catch((reason) => {
                          ctx.webex.logger.error(`Decrypt share.microsoftSharedLinkInfo.itemId error for activity ${activity.activityId} in space ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`);
                          // add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
                          activity.error = reason;
  
                          return Promise.resolve(object);
                        }));
                    }
                  }
  
                  // Decrypt the scr (Secure Content Reference) or sslr (Secure Shared Link Reference)
                  // Unlike a scr the sslr contains only a loc. But decryptScr(...) is flexible and
                  // leaves the tag, auth, IV, etc fields on the SCR object as undefined.
                  if (share.scr || share.sslr) {
                    promises.push(requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptScr,
                      [activity.encryptionKeyUrl, share.scr || share.sslr, {onBehalfOf: spaceParticipantId}])
                      .then((decryptedSCR) => {
                        if (share.scr) {
                          share.scr = decryptedSCR;
                        }
                        else {
                          share.sslr = decryptedSCR.loc;
                        }
                      })
                      .catch((reason) => {
                        ctx.webex.logger.error(`Decrypt file scr or sslr error for activity ${activity.activityId} in space ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`);
                        // add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
                        activity.error = reason;
  
                        return Promise.resolve(object);
                      }));
                  }
                }
  
                return Promise.all(promises);
              })
              .catch((reason) => {
                ctx.webex.logger.error(`Error retrieving space summary for: ${activity.activityId} in space ${activity.targetId}: ${reason}`);
                // add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
                activity.error = reason;
  
                return Promise.resolve(object);
              });
          }
        },
        {
          name: 'decryptReportContentArray',
          direction: 'inbound',
          fn(ctx, object) {
            if (!object || !object.body) {
              return Promise.resolve();
            }
            // the report id is needed in order to retrieve space information for activities with whiteboards so that
            // the list of users in those spaces can be accessed and the content can be decrypted on their behalf
            const reportId = object.options.resource.split('/')[1];
  
            return Promise.all(object.body.map((item) => ctx.transform('decryptReportContent', {body: item}, reportId)));
          }
        },
        {
          name: 'decryptReportContentSummary',
          direction: 'inbound',
          fn(ctx, object) {
            if (!object || !object.body || !object.body.spaceName || !object.body.participants || !object.body.participants.length) {
              return Promise.resolve();
            }
            const spaceSummary = object.body;
  
            if (!spaceSummary.spaceName) {
              return Promise.resolve(object);
            }
  
            if (!spaceSummary.participants || !spaceSummary.participants.length) {
              const reason = `Known error - detected space with no participant - unable to decrypt name for space ${spaceSummary.spaceId}`;
  
              ctx.webex.logger.error(reason);
              spaceSummary.error = reason;
  
              return Promise.resolve(object);
            }
  
            return requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
              [spaceSummary.encryptionKeyUrl, spaceSummary.spaceName, {onBehalfOf: spaceSummary.participants[0]}])
              .then((decryptedSpaceName) => {
                spaceSummary.spaceName = decryptedSpaceName;
              })
              .catch((reason) => {
                ctx.webex.logger.error(`Decrypt space name error for space ${spaceSummary.spaceId}: ${reason}`);
                // add error property to space info - this error will be recorded in the downloader
                spaceSummary.error = reason;
  
                return Promise.resolve(object);
              });
          }
        },
        {
          name: 'decryptReportContentSummaryArray',
          direction: 'inbound',
          fn(ctx, object) {
            if (!object || !object.body) {
              return Promise.resolve();
            }
  
            return Promise.all(object.body.map((item) => ctx.transform('decryptReportContentSummary', {body: item})));
          }
        }
      ]
    }
  });
  
  export default EDiscovery;
  
  export {config} from './config';
  export {default as ReportRequest} from './report-request';
  export {EdiscoveryError, InvalidEmailAddressError} from './ediscovery-error';
  