/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.exoplatform.social.notification.channel.template;

import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import io.meeds.social.notification.plugin.JoinedSpaceByInvitationLinkPlugin;
import io.meeds.social.notification.util.NotificationUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import org.exoplatform.commons.api.notification.NotificationContext;
import org.exoplatform.commons.api.notification.annotation.TemplateConfig;
import org.exoplatform.commons.api.notification.annotation.TemplateConfigs;
import org.exoplatform.commons.api.notification.channel.template.AbstractTemplateBuilder;
import org.exoplatform.commons.api.notification.channel.template.TemplateProvider;
import org.exoplatform.commons.api.notification.model.MessageInfo;
import org.exoplatform.commons.api.notification.model.NotificationInfo;
import org.exoplatform.commons.api.notification.model.PluginKey;
import org.exoplatform.commons.api.notification.plugin.NotificationPluginUtils;
import org.exoplatform.commons.api.notification.service.template.TemplateContext;
import org.exoplatform.commons.notification.template.TemplateUtils;
import org.exoplatform.commons.utils.CommonsUtils;
import org.exoplatform.commons.utils.HTMLEntityEncoder;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.social.core.activity.model.ExoSocialActivity;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.identity.model.Profile;
import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.processor.I18NActivityProcessor;
import org.exoplatform.social.core.relationship.model.Relationship;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.utils.MentionUtils;
import org.exoplatform.social.notification.LinkProviderUtils;
import org.exoplatform.social.notification.Utils;
import org.exoplatform.social.notification.plugin.*;
import org.jsoup.Jsoup;

@TemplateConfigs(templates = {
    @TemplateConfig(pluginId = ActivityReplyToCommentPlugin.ID, template = "war:/notification/templates/ActivityReplyToCommentPlugin.gtmpl"),
    @TemplateConfig(pluginId = ActivityCommentPlugin.ID, template = "war:/notification/templates/ActivityCommentPlugin.gtmpl"),
    @TemplateConfig(pluginId = ActivityCommentWatchPlugin.ID, template = "war:/notification/templates/ActivityCommentPlugin.gtmpl"),
    @TemplateConfig(pluginId = EditActivityPlugin.ID, template = "war:/notification/templates/EditActivityPlugin.gtmpl"),
    @TemplateConfig(pluginId = EditCommentPlugin.ID, template = "war:/notification/templates/EditCommentPlugin.gtmpl"),
    @TemplateConfig(pluginId = ActivityMentionPlugin.ID, template = "war:/notification/templates/ActivityMentionPlugin.gtmpl"),
    @TemplateConfig(pluginId = LikePlugin.ID, template = "war:/notification/templates/LikePlugin.gtmpl"),
    @TemplateConfig(pluginId = LikeCommentPlugin.ID, template = "war:/notification/templates/LikeCommentPlugin.gtmpl"),
    @TemplateConfig(pluginId = NewUserPlugin.ID, template = "war:/notification/templates/NewUserPlugin.gtmpl"),
    @TemplateConfig(pluginId = PostActivityPlugin.ID, template = "war:/notification/templates/PostActivityPlugin.gtmpl"),
    @TemplateConfig(pluginId = PostActivitySpaceStreamPlugin.ID, template = "war:/notification/templates/PostActivitySpaceStreamPlugin.gtmpl"),
    @TemplateConfig(pluginId = RelationshipReceivedRequestPlugin.ID, template = "war:/notification/templates/RelationshipReceivedRequestPlugin.gtmpl"),
    @TemplateConfig(pluginId = SharedActivitySpaceStreamPlugin.ID, template = "war:/notification/templates/SharedActivitySpaceStreamPlugin.gtmpl"),
    @TemplateConfig(pluginId = RequestJoinSpacePlugin.ID, template = "war:/notification/templates/RequestJoinSpacePlugin.gtmpl"),
    @TemplateConfig(pluginId = SpaceInvitationPlugin.ID, template = "war:/notification/templates/SpaceInvitationPlugin.gtmpl"),
    @TemplateConfig(pluginId = JoinedSpaceByInvitationLinkPlugin.ID, template = "war:/notification/templates/JoinedSpaceByInvitationLinkPlugin.gtmpl")})

public class MailTemplateProvider extends TemplateProvider {

  private static final Log LOG = ExoLogger.getLogger(MailTemplateProvider.class);

  /** Defines the template builder for ActivityReplyToCommentPlugin*/
  private AbstractTemplateBuilder replyToComment = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();
      NotificationInfo notification = ctx.getNotificationInfo();
      String language = getLanguage(notification);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      String commentId = notification.getValueOwnerParameter(SocialNotificationUtils.COMMENT_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      ExoSocialActivity commentActivity = Utils.getActivityManager().getActivity(commentId);
      ExoSocialActivity parentCommentActivity = Utils.getActivityManager().getActivity(commentActivity.getParentCommentId());
      Identity identity = Utils.getIdentityManager().getIdentity(commentActivity.getPosterId(), true);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      templateContext.put("USER", Utils.addExternalFlag(identity));
      String subject = TemplateUtils.processSubject(templateContext);

      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);
      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
      String subCommentTitle = SocialNotificationUtils.processImageTitle(getActivityTitle(commentActivity, language), imagePlaceHolder);
      String commentTitle = SocialNotificationUtils.processImageTitle(getActivityTitle(parentCommentActivity, language), imagePlaceHolder);
      templateContext.put("COMMENT_REPLY", processBody(subCommentTitle, language));
      templateContext.put("COMMENT", processBody(commentTitle, language));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity_highlight_comment_reply", activity.getId() + "-" + parentCommentActivity.getId() + "-" + commentActivity.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity_highlight_comment_reply", activity.getId() + "-" + parentCommentActivity.getId() + "-" + commentActivity.getId()));

      String body = SocialNotificationUtils.getBody(ctx, templateContext, parentCommentActivity);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());
      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(first.getTo(), templateContext);
      
      //Store the activity id as key, and the list all identities who posted to the activity.
      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();
      Map<String, List<Pair<String, String>>> activityUserComments = new LinkedHashMap<String, List<Pair<String, String>>>();
      
      try {
        String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
        for (NotificationInfo message : notifications) {
          String commentId = message.getValueOwnerParameter(SocialNotificationUtils.COMMENT_ID.getKey());
          ExoSocialActivity commentActivity = Utils.getActivityManager().getActivity(commentId);
          if (commentActivity == null || commentActivity.getParentCommentId() == null) {
            continue;
          }

          ExoSocialActivity parentCommentActivity = Utils.getActivityManager().getActivity(commentActivity.getParentCommentId());
          if (!parentCommentActivity.isComment()) {
            continue;
          }
          Identity identity = Utils.getIdentityManager().getIdentity(parentCommentActivity.getPosterId(), true);
          if (identity == null || StringUtils.isBlank(message.getTo()) || !message.getTo().equals(identity.getRemoteId())) {
            continue;
          }
          String poster = message.getValueOwnerParameter("poster");
          String title = SocialNotificationUtils.processImageTitle(getActivityTitle(commentActivity, language), imagePlaceHolder);
          Pair<String, String> userComment = new ImmutablePair<String, String>(poster, title);
          ExoSocialActivity parentActivity = Utils.getActivityManager().getParentActivity(commentActivity);
          //
          SocialNotificationUtils.processInforSendTo(receiverMap, parentActivity.getId(), poster);
          SocialNotificationUtils.processInforUserComments(activityUserComments, parentActivity.getId(), userComment);
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, activityUserComments, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      
      return true;
    }
    
  };
  
  /** Defines the template builder for ActivityCommentPlugin*/
  private AbstractTemplateBuilder comment = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();
      NotificationInfo notification = ctx.getNotificationInfo();
      String language = getLanguage(notification);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      String commentId = notification.getValueOwnerParameter(SocialNotificationUtils.COMMENT_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      ExoSocialActivity comment = null;
      if (StringUtils.isNotBlank(commentId)) {
        comment = Utils.getActivityManager().getActivity(commentId);
      }
      if (activity == null) {
        LOG.debug("Activity with id '{}' was removed but the notification with id'{}' is remaining", activityId, notification.getId());
        return null;
      }
      if(activity.isComment()) {
        comment = Utils.getActivityManager().getParentActivity(activity);
      }
      if (comment == null) {
        LOG.debug("Comment of activity with id '{}' was removed but the notification with id'{}' is remaining", commentId, notification.getId());
        return null;
      }
      Identity identity = Utils.getIdentityManager().getIdentity(comment.getPosterId(), true);

      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      templateContext.put("USER", Utils.addExternalFlag(identity));
      String subject = TemplateUtils.processSubject(templateContext);

      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);
      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));

      String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
      String title = SocialNotificationUtils.processImageTitle(getActivityTitle(comment, language), imagePlaceHolder);
      templateContext.put("COMMENT", processBody(title, language));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(comment));
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity_highlight_comment", activity.getId() + "-" + comment.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity_highlight_comment", activity.getId() + "-" + comment.getId()));

      String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());
      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(first.getTo(), templateContext);

      //Store the activity id as key, and the list all identities who posted to the activity.
      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();
      Map<String, List<Pair<String, String>>> activityUserComments = new LinkedHashMap<String, List<Pair<String, String>>>();


      try {
        String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
        for (NotificationInfo message : notifications) {
          String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
          if (activity == null) {
            continue;
          }
          String commentId = message.getValueOwnerParameter(SocialNotificationUtils.COMMENT_ID.getKey());
          ExoSocialActivity parentActivity = null;
          if(StringUtils.isBlank(commentId)) {
            LOG.warn("Attempt to send a mail message with id '{}' and receiver '{}' with empty parameter 'commentId' and activityId = '{}' ",
                     message.getId(),
                     message.getTo(),
                     activityId);
          } else {
            parentActivity = activity;
            activity = Utils.getActivityManager().getActivity(commentId);
            if (activity == null) {
              continue;
            }
          }

          String poster = message.getValueOwnerParameter("poster");
          if(message.getTo() != null && poster != null && poster.equals(message.getTo())) {
            continue;
          }
          String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
          Pair<String, String> userComment = new ImmutablePair<String, String>(poster, title);
          if (parentActivity.getStreamOwner() != null) {
            Identity spaceIdentity = Utils.getIdentityManager().getOrCreateIdentity(SpaceIdentityProvider.NAME, parentActivity.getStreamOwner(), true);
            if (spaceIdentity == null) {
              if (message.getTo()!=null && !message.getTo().equals(parentActivity.getStreamOwner())) {
                continue;
              }
            } else if (parentActivity.getPosterId() != null) {
              Identity identity = Utils.getIdentityManager().getIdentity(parentActivity.getPosterId(), true);
              if (identity != null) {
                if (message.getTo() != null && !message.getTo().equals(identity.getRemoteId())) {
                  continue;
                }
              }
            }
          }
          //
          SocialNotificationUtils.processInforSendTo(receiverMap, parentActivity.getId(), poster);
          SocialNotificationUtils.processInforUserComments(activityUserComments, parentActivity.getId(), userComment);
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, activityUserComments, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }

      return true;
    }

  };

  /** Defines the template builder for EditCommentPlugin*/
  private AbstractTemplateBuilder editComment = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
        MessageInfo messageInfo = new MessageInfo();
        NotificationInfo notification = ctx.getNotificationInfo();
        String language = getLanguage(notification);

        String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
        String commentId = notification.getValueOwnerParameter(SocialNotificationUtils.COMMENT_ID.getKey());
        ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
        ExoSocialActivity comment = null;
        if (StringUtils.isNotBlank(commentId)) {
            comment = Utils.getActivityManager().getActivity(commentId);
        }
        if (activity == null) {
            LOG.debug("Activity with id '{}' was removed but the notification with id'{}' is remaining", activityId, notification.getId());
            return null;
        }
        if(activity.isComment()) {
            comment = Utils.getActivityManager().getParentActivity(activity);
        }
        if (comment == null) {
            LOG.debug("Comment of activity with id '{}' was removed but the notification with id'{}' is remaining", commentId, notification.getId());
            return null;
        }
        Identity identity = Utils.getIdentityManager().getIdentity(comment.getPosterId(), true);

        TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
        templateContext.put("USER", Utils.addExternalFlag(identity));
        String subject = TemplateUtils.processSubject(templateContext);

        SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);
        templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));

        String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
        String title = SocialNotificationUtils.processImageTitle(getActivityTitle(comment, language), imagePlaceHolder);
        templateContext.put("COMMENT", processBody(title, language));
        templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(comment));
        templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity_highlight_comment", activity.getId() + "-" + comment.getId()));
        templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity_highlight_comment", activity.getId() + "-" + comment.getId()));

        String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);
        //binding the exception throws by processing template
        ctx.setException(templateContext.getException());
        return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(first.getTo(), templateContext);

      //Store the activity id as key, and the list all identities who posted to the activity.
      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();
      Map<String, List<Pair<String, String>>> activityUserComments = new LinkedHashMap<String, List<Pair<String, String>>>();


      try {
        String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
        for (NotificationInfo message : notifications) {
          String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
          if (activity == null) {
            continue;
          }
          String commentId = message.getValueOwnerParameter(SocialNotificationUtils.COMMENT_ID.getKey());
          ExoSocialActivity parentActivity = activity;
          if(StringUtils.isBlank(commentId)) {
            LOG.warn("Attempt to send a mail message with id '{}' and receiver '{}' with empty parameter 'commentId' and activityId = '{}' ",
                    message.getId(),
                    message.getTo(),
                    activityId);
          } else {
            parentActivity = activity;
            activity = Utils.getActivityManager().getActivity(commentId);
            if (activity == null) {
              continue;
            }
          }

          String poster = message.getValueOwnerParameter("poster");
          if(message.getTo() != null && poster != null && poster.equals(message.getTo())) {
            continue;
          }
          String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
          Pair<String, String> userComment = new ImmutablePair<String, String>(poster, title);
          if (parentActivity.getStreamOwner() != null) {
            Identity spaceIdentity = Utils.getIdentityManager().getOrCreateIdentity(SpaceIdentityProvider.NAME, parentActivity.getStreamOwner(), true);
            if (spaceIdentity == null) {
              if (message.getTo()!=null && !message.getTo().equals(parentActivity.getStreamOwner())) {
                continue;
              }
            } else if (parentActivity.getPosterId() != null) {
              Identity identity = Utils.getIdentityManager().getIdentity(parentActivity.getPosterId(), true);
              if (identity != null) {
                if (message.getTo() != null && !message.getTo().equals(identity.getRemoteId())) {
                  continue;
                }
              }
            }
          }
          //
          SocialNotificationUtils.processInforSendTo(receiverMap, parentActivity.getId(), poster);
          SocialNotificationUtils.processInforUserComments(activityUserComments, parentActivity.getId(), userComment);
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, activityUserComments, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }

      return true;
    }

  };

    /** Defines the template builder for EditActivityPlugin*/
    private AbstractTemplateBuilder editActivity = new AbstractTemplateBuilder() {
        @Override
        protected MessageInfo makeMessage(NotificationContext ctx) {
            MessageInfo messageInfo = new MessageInfo();
            NotificationInfo notification = ctx.getNotificationInfo();
            String language = getLanguage(notification);

            String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
            ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
            if (activity == null) {
                LOG.debug("Activity with id '{}' was removed but the notification with id'{}' is remaining", activityId, notification.getId());
                return null;
            }

            Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);

            TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
            templateContext.put("USER", Utils.addExternalFlag(identity));
            String subject = TemplateUtils.processSubject(templateContext);

            SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);
            templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));

            String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
            String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
            templateContext.put("COMMENT", processBody(title, language));
            templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
            templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activityId));
            templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activityId));

            String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);
            //binding the exception throws by processing template
            ctx.setException(templateContext.getException());
            return messageInfo.subject(subject).body(body).end();
        }

        @Override
        protected boolean makeDigest(NotificationContext ctx, Writer writer) {
            List<NotificationInfo> notifications = ctx.getNotificationInfos();
            NotificationInfo first = notifications.get(0);

            String language = getLanguage(first);
            TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
            SocialNotificationUtils.addFooterAndFirstName(first.getTo(), templateContext);

            //Store the activity id as key, and the list all identities who posted to the activity.
            Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();
            Map<String, List<Pair<String, String>>> activityUserComments = new LinkedHashMap<String, List<Pair<String, String>>>();


            try {
                String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
                for (NotificationInfo message : notifications) {
                    String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
                    ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
                    if (activity == null) {
                        continue;
                    }

                    String poster = message.getValueOwnerParameter("poster");
                    if(message.getTo() != null && poster != null && poster.equals(message.getTo())) {
                        continue;
                    }
                    String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
                    Pair<String, String> userComment = new ImmutablePair<String, String>(poster, title);

                    SocialNotificationUtils.processInforSendTo(receiverMap, activity.getId(), poster);
                    SocialNotificationUtils.processInforUserComments(activityUserComments, activity.getId(), userComment);
                }
                writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, activityUserComments, templateContext));
            } catch (IOException e) {
                ctx.setException(e);
                return false;
            }

            return true;
        }

    };

  /** Defines the template builder for ActivityMentionPlugin*/
  private AbstractTemplateBuilder mention = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();
      String language = getLanguage(notification);

      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);

      templateContext.put("USER", Utils.addExternalFlag(identity));
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("AVATAR", LinkProviderUtils.getUserAvatarUrl(identity.getProfile()));
      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      String body = "";

      // In case of mention on a comment, we need provide the id of the activity, not of the comment
      ExoSocialActivity i18nActivity = getI18N(activity,new Locale(language));
      if (activity.isComment()) {
        ExoSocialActivity parentActivity = Utils.getActivityManager().getParentActivity(activity);
        activityId = parentActivity.getId();
        templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity_highlight_comment", activityId + "-" + activity.getId()));
        templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity_highlight_comment", activityId + "-" + activity.getId()));
        String title = getActivityTitle(i18nActivity, language);
        String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
        title = SocialNotificationUtils.processImageTitle(title, imagePlaceHolder);
        templateContext.put("ACTIVITY", processBody(title, language));
        body = TemplateUtils.processGroovy(templateContext);
      } else {
        templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activityId));
        templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activityId));
        body = SocialNotificationUtils.getBody(ctx, templateContext, i18nActivity);
      }

      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());
      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);

      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();
      try {
        for (NotificationInfo notification : notifications) {
          String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
          if (activity == null) {
            continue;
          }
          List<String> mentionedUsers = new LinkedList<String>();
          for (String id : activity.getMentionedIds()) {
            if (id.contains("@")) {
              id = id.substring(0, id.length() - 2);
            }
            Identity identity = Utils.getIdentityManager().getIdentity(id, true);
            if (identity != null) {
              mentionedUsers.add(identity.getRemoteId());
            }
          }
          if (notification.getTo() != null && !mentionedUsers.contains(notification.getTo())) {
            continue;
          }
          Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);

          if (activity.isComment()) {
            activity = Utils.getActivityManager().getParentActivity(activity);
          }

          //make the list receivers who will send mail to them.
          SocialNotificationUtils.processInforSendTo(receiverMap, activity.getId(), identity.getRemoteId());
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }

      return true;
    }

  };

  /** Defines the template builder for LikePlugin*/
  private AbstractTemplateBuilder like = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      Identity identity = Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, notification.getValueOwnerParameter("likersId"), true);
      if (identity == null) {
        return null;
      }

      templateContext.put("USER", Utils.addExternalFlag(identity));
      String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
      String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
      String cleanedTitle = StringEscapeUtils.unescapeHtml4(title);
      templateContext.put("SUBJECT", cleanedTitle);
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activity.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activity.getId()));
      String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);

      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());
      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);

      Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();

      try {
        for (NotificationInfo message : notifications) {
          String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());

          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);

          //
          if (activity == null) {
            continue;
          }

          if (activity.getPosterId() != null) {
            Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);
            if (identity != null) {
              if (message.getTo() != null && !message.getTo().equals(identity.getRemoteId())) {
                continue;
              }
            }
          }

          //
          String fromUser = message.getValueOwnerParameter("likersId");

          Identity identityFrom = Utils.getService(IdentityManager.class).getOrCreateIdentity(OrganizationIdentityProvider.NAME, fromUser, false);
          if (identityFrom == null || !Arrays.asList(activity.getLikeIdentityIds()).contains(identityFrom.getId())) {
            continue;
          }
          //
          SocialNotificationUtils.processInforSendTo(map, activityId, message.getValueOwnerParameter("likersId"));
        }
        writer.append(SocialNotificationUtils.getMessageByIds(map, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }


      return true;
    }

  };

  /** Defines the template builder for LikeCommentPlugin*/
  private AbstractTemplateBuilder likeComment = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      Identity identity = Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, notification.getValueOwnerParameter("likersId"), true);
      if (identity == null) {
        return null;
      }

      templateContext.put("USER", Utils.addExternalFlag(identity));
      String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
      String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
      String cleanedTitle = StringEscapeUtils.unescapeHtml4(title);
      templateContext.put("SUBJECT", cleanedTitle);
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      ExoSocialActivity activityOfComment = Utils.getActivityManager().getParentActivity(activity);
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activityOfComment.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activityOfComment.getId()));
      ExoSocialActivity i18nComment = getI18N(activity, new Locale(language));
      String commentTitle = getActivityTitle(i18nComment, language);
      commentTitle = SocialNotificationUtils.processImageTitle(commentTitle, imagePlaceHolder);
      templateContext.put("ACTIVITY", processBody(commentTitle, language));
      String body = TemplateUtils.processGroovy(templateContext);

      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());
      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);
      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
      try {
        for (NotificationInfo message : notifications) {
          String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
          //
          if (activity == null) {
            continue;
          }
          if (activity.getPosterId() != null) {
            Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);
            if (identity != null) {
              if (message.getTo() != null && !message.getTo().equals(identity.getRemoteId())) {
                continue;
              }
            }
          }
          //
          String fromUser = message.getValueOwnerParameter("likersId");
          Identity identityFrom = Utils.getService(IdentityManager.class).getOrCreateIdentity(OrganizationIdentityProvider.NAME, fromUser, false);
          if (identityFrom == null || !Arrays.asList(activity.getLikeIdentityIds()).contains(identityFrom.getId())) {
            continue;
          }
          //
          SocialNotificationUtils.processInforSendTo(map, activityId, message.getValueOwnerParameter("likersId"));
        }
        writer.append(SocialNotificationUtils.getMessageByIds(map, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      return true;
    }
    
  };

  /** Defines the template builder for NewUserPlugin*/
  private AbstractTemplateBuilder newUser = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String remoteId = notification.getValueOwnerParameter(SocialNotificationUtils.REMOTE_ID.getKey());
      Identity identity = Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, remoteId, true);
      if (identity == null) {
        return null;
      }
      Profile userProfile = identity.getProfile();

      templateContext.put("USER", Utils.addExternalFlag(identity));
      templateContext.put("PORTAL_NAME", NotificationPluginUtils.getBrandingPortalName());
      templateContext.put("PORTAL_HOME", org.exoplatform.commons.notification.NotificationUtils.getPortalHome(NotificationPluginUtils.getBrandingPortalName()));
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("AVATAR", LinkProviderUtils.getUserAvatarUrl(userProfile));
      templateContext.put("CONNECT_ACTION_URL", LinkProviderUtils.getInviteToConnectUrl(identity.getRemoteId(), notification.getTo()));
      String body = TemplateUtils.processGroovy(templateContext);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());

      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);

      Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
      try {
        for (NotificationInfo message : notifications) {
          String remoteId = message.getValueOwnerParameter(SocialNotificationUtils.REMOTE_ID.getKey());

          Identity identity = Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, remoteId);
          if (identity == null) {
            continue;
          }
          //
          if (identity.isDeleted() == true) {
            continue;
          }

          SocialNotificationUtils.processInforSendTo(map, first.getKey().getId(), remoteId);
        }

        templateContext.put("PORTAL_NAME", NotificationPluginUtils.getBrandingPortalName());
        templateContext.put("PORTAL_HOME", SocialNotificationUtils.buildRedirecUrl("portal_home", "home", ""));

        writer.append(SocialNotificationUtils.getMessageByIds(map, templateContext, "new_user"));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }

      return true;
    }

  };

  /** Defines the template builder for PostActivityPlugin*/
  private AbstractTemplateBuilder postActivity = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);


      templateContext.put("USER", Utils.addExternalFlag(identity));
      String imagePlaceHolder = SocialNotificationUtils.getImagePlaceHolder(language);
      String title = SocialNotificationUtils.processImageTitle(getActivityTitle(activity, language), imagePlaceHolder);
      templateContext.put("SUBJECT", title);
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activity.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activity.getId()));

      String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());

      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);
      String sendToUser = first.getTo();
      String language = getLanguage(first);

      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();

      try {
        for (NotificationInfo message : notifications) {
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey()));

          //Case of activity was deleted, ignore this notification
          if (activity == null || !activity.getStreamOwner().equals(message.getTo())) {
            continue;
          }
          SocialNotificationUtils.processInforSendTo(receiverMap, sendToUser, message.getValueOwnerParameter(SocialNotificationUtils.POSTER.getKey()));
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, templateContext, "user"));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      return true;
    }

  };

  /** Defines the template builder for PostActivitySpaceStreamPlugin*/
  private AbstractTemplateBuilder postActivitySpace = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      String title = getActivityTitle(activity, language);
      String cleanedTitle = StringEscapeUtils.unescapeHtml4(title);
      Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId(), true);

      Identity spaceIdentity = Utils.getIdentityManager().getOrCreateIdentity(SpaceIdentityProvider.NAME, activity.getStreamOwner(), true);
      if (spaceIdentity == null) {
        return null;
      }
      Space space = Utils.getSpaceService().getSpaceByPrettyName(spaceIdentity.getRemoteId());
      if (space == null) {
        return null;
      }

      templateContext.put("USER", Utils.addExternalFlag(identity));
      templateContext.put("SPACE", space.getDisplayName());

      templateContext.put("SUBJECT", cleanedTitle);
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("SPACE_URL", LinkProviderUtils.getRedirectUrl("space", space.getId()));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activity.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activity.getId()));

      String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());

      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);

      Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();

      try {
        for (NotificationInfo message : notifications) {
          String poster = message.getValueOwnerParameter(SocialNotificationUtils.POSTER.getKey());
          String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
          if (activity == null) {
            continue;
          }
          Space space = Utils.getSpaceService().getSpaceByPrettyName(activity.getStreamOwner());
          if (space == null) {
            continue;
          }
          if (!Arrays.asList(space.getMembers()).contains(message.getTo())) {
            continue;
          }
          if(message.getTo() != null && poster != null && poster.equals(message.getTo())) {
            continue;
          }
          //
          SocialNotificationUtils.processInforSendTo(map, space.getId(), poster);
        }
        writer.append(SocialNotificationUtils.getMessageInSpace(map, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      return true;
    }

  };

  /** Defines the template builder for PostActivitySpaceStreamPlugin*/
  private AbstractTemplateBuilder shareActivitySpace = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String activityId = notification.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
      String originalTitle = notification.getValueOwnerParameter(SocialNotificationUtils.ORIGINAL_TITLE.getKey());
      originalTitle = MentionUtils.substituteRoleWithLocale(originalTitle, Locale.forLanguageTag(language));
      String sharedTitle = Jsoup.parse(notification.getValueOwnerParameter(SocialNotificationUtils.ORIGINAL_TITLE_SHARED.getKey())).text();
      ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
      Identity identity = Utils.getIdentityManager().getIdentity(activity.getPosterId());

      Identity spaceIdentity = Utils.getIdentityManager().getOrCreateIdentity(SpaceIdentityProvider.NAME, activity.getStreamOwner());
      if (spaceIdentity == null) {
        return null;
      }
      Space space = Utils.getSpaceService().getSpaceByPrettyName(spaceIdentity.getRemoteId());
      if (space == null) {
        return null;
      }
      templateContext.put("USER", Utils.addExternalFlag(identity));
      templateContext.put("SPACE", space.getDisplayName());
      templateContext.put("SHORT_TITLE", sharedTitle.substring(0, sharedTitle.length() < 10 ? sharedTitle.length() :   sharedTitle.length()/2 ) + "...");
      templateContext.put("SUBJECT", originalTitle);
      templateContext.put("TITLE", sharedTitle);
      templateContext.put("IMAGE", CommonsUtils.getCurrentDomain() +"/news/images/news.png");
      templateContext.put("SPACE_URL", LinkProviderUtils.getRedirectUrl("space", space.getId()));
      templateContext.put("OPEN_URL", LinkProviderUtils.getOpenLink(activity));
      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("REPLY_ACTION_URL", LinkProviderUtils.getRedirectUrl("reply_activity", activity.getId()));
      templateContext.put("VIEW_FULL_DISCUSSION_ACTION_URL", LinkProviderUtils.getRedirectUrl("view_full_activity", activity.getId()));
      String subject = TemplateUtils.processSubject(templateContext);
      String body = SocialNotificationUtils.getBody(ctx, templateContext, activity);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());

      MessageInfo messageInfo = new MessageInfo();

      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);

      Map<String, List<String>> map = new LinkedHashMap<>();

      try {
        for (NotificationInfo message : notifications) {
          String poster = message.getValueOwnerParameter(SocialNotificationUtils.POSTER.getKey());
          String activityId = message.getValueOwnerParameter(SocialNotificationUtils.ACTIVITY_ID.getKey());
          ExoSocialActivity activity = Utils.getActivityManager().getActivity(activityId);
          if (activity != null) {
            Space space = Utils.getSpaceService().getSpaceByPrettyName(activity.getStreamOwner());
            if (space != null) {
              if (!Arrays.asList(space.getMembers()).contains(message.getTo())) {
                continue;
              }
              if(message.getTo() != null && poster != null && poster.equals(message.getTo())) {
                continue;
              }
              SocialNotificationUtils.processInforSendTo(map, space.getId(), poster);
            }
          }
        }
        writer.append(SocialNotificationUtils.getMessageInSpace(map, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      return true;
    }

  };


  /** Defines the template builder for RelationshipReceivedRequestPlugin*/
  private AbstractTemplateBuilder relationshipReceived = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);

      String sender = notification.getValueOwnerParameter("sender");
      String toUser = notification.getTo();
      SocialNotificationUtils.addFooterAndFirstName(toUser, templateContext);
      Identity identity = Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, sender, true);
      Profile userProfile = identity.getProfile();

      templateContext.put("PORTAL_NAME", NotificationPluginUtils.getBrandingPortalName());
      templateContext.put("USER", Utils.addExternalFlag(identity));
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("AVATAR", LinkProviderUtils.getUserAvatarUrl(userProfile));
      templateContext.put("ACCEPT_CONNECTION_REQUEST_ACTION_URL", LinkProviderUtils.getConfirmInvitationToConnectUrl(sender, toUser));
      templateContext.put("REFUSE_CONNECTION_REQUEST_ACTION_URL", LinkProviderUtils.getIgnoreInvitationToConnectUrl(sender, toUser));
      String body = TemplateUtils.processGroovy(templateContext);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());
      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);
      String language = getLanguage(first);

      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();

      try {
        for (NotificationInfo message : notifications) {
          Relationship relationship = Utils.getRelationshipManager().get(message.getValueOwnerParameter(SocialNotificationUtils.RELATIONSHIP_ID.getKey()));
          if (relationship == null || relationship.getStatus().name().equals("PENDING") == false || !relationship.getReceiver().getRemoteId().equals(message.getTo())) {
            continue;
          }
          SocialNotificationUtils.processInforSendTo(receiverMap, first.getTo(), message.getValueOwnerParameter(SocialNotificationUtils.SENDER.getKey()));
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, templateContext, "connections_request"));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      return true;
    }

  };

  /** Defines the template builder for RequestJoinSpacePlugin*/
  private AbstractTemplateBuilder requestJoinSpace = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String spaceId = notification.getValueOwnerParameter(SocialNotificationUtils.SPACE_ID.getKey());
      Space space = Utils.getSpaceService().getSpaceById(spaceId);
      if (space == null) {
        return null;
      }
      Identity identity = Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, notification.getValueOwnerParameter("request_from"), true);
      if (identity == null) {
        return null;
      }
      Profile userProfile = identity.getProfile();

      templateContext.put("SPACE", space.getDisplayName());
      templateContext.put("USER", Utils.addExternalFlag(identity));
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("SPACE_URL", LinkProviderUtils.getRedirectUrl("space_members", space.getId()));
      templateContext.put("PROFILE_URL", LinkProviderUtils.getRedirectUrl("user", identity.getRemoteId()));
      templateContext.put("AVATAR", LinkProviderUtils.getUserAvatarUrl(userProfile));
      templateContext.put("VALIDATE_SPACE_REQUEST_ACTION_URL", LinkProviderUtils.getValidateRequestToJoinSpaceUrl(space.getId(), identity.getRemoteId()));
      templateContext.put("REFUSE_SPACE_REQUEST_ACTION_URL", LinkProviderUtils.getRefuseRequestToJoinSpaceUrl(space.getId(), identity.getRemoteId()));
      String body = TemplateUtils.processGroovy(templateContext);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());

      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);

      String language = getLanguage(first);
      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);

      Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();

      try {
        for (NotificationInfo message : notifications) {
          String spaceId = message.getValueOwnerParameter(SocialNotificationUtils.SPACE_ID.getKey());
          String fromUser = message.getValueOwnerParameter("request_from");
          Space space = Utils.getSpaceService().getSpaceById(spaceId);
          if (space == null) {
            continue;
          }
          if (ArrayUtils.contains(space.getPendingUsers(), fromUser) == false || !ArrayUtils.contains(space.getManagers(), message.getTo())) {
            continue;
          }
          //
          SocialNotificationUtils.processInforSendTo(map, spaceId, fromUser);
        }
        writer.append(SocialNotificationUtils.getMessageInSpace(map, templateContext));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }
      return true;
    }

  };

  /** Defines the template builder for SpaceInvitationPlugin*/
  private AbstractTemplateBuilder spaceInvitation = new AbstractTemplateBuilder() {
    @Override
    protected MessageInfo makeMessage(NotificationContext ctx) {
      MessageInfo messageInfo = new MessageInfo();

      NotificationInfo notification = ctx.getNotificationInfo();

      String language = getLanguage(notification);
      TemplateContext templateContext = new TemplateContext(notification.getKey().getId(), language);
      SocialNotificationUtils.addFooterAndFirstName(notification.getTo(), templateContext);

      String spaceId = notification.getValueOwnerParameter(SocialNotificationUtils.SPACE_ID.getKey());
      Space space = Utils.getSpaceService().getSpaceById(spaceId);
      if (space == null) {
        return null;
      }

      templateContext.put("SPACE", space.getDisplayName());
      templateContext.put("SPACE_URL", LinkProviderUtils.getRedirectUrl("space", space.getId()));
      String subject = TemplateUtils.processSubject(templateContext);

      templateContext.put("SPACE_AVATAR", LinkProviderUtils.getSpaceAvatarUrl(space));
      templateContext.put("ACCEPT_SPACE_INVITATION_ACTION_URL", LinkProviderUtils.getAcceptInvitationToJoinSpaceUrl(space.getId(), notification.getTo()));
      templateContext.put("REFUSE_SPACE_INVITATION_ACTION_URL", LinkProviderUtils.getIgnoreInvitationToJoinSpaceUrl(space.getId(), notification.getTo()));
      String body = TemplateUtils.processGroovy(templateContext);
      //binding the exception throws by processing template
      ctx.setException(templateContext.getException());

      return messageInfo.subject(subject).body(body).end();
    }

    @Override
    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
      List<NotificationInfo> notifications = ctx.getNotificationInfos();
      NotificationInfo first = notifications.get(0);
      String language = getLanguage(first);

      TemplateContext templateContext = new TemplateContext(first.getKey().getId(), language);
      Map<String, List<String>> receiverMap = new LinkedHashMap<String, List<String>>();

      try {
        for (NotificationInfo message : notifications) {
          String spaceId = message.getValueOwnerParameter(SocialNotificationUtils.SPACE_ID.getKey());
          Space space = Utils.getSpaceService().getSpaceById(spaceId);
          if(space == null) {
            LOG.info("Can't find space with id '{}'. Mail notification with id '{}' will not be sent", spaceId, message.getId());
            continue;
          }
          if (ArrayUtils.contains(space.getInvitedUsers(), first.getTo()) == false) {
            continue;
          }

          SocialNotificationUtils.processInforSendTo(receiverMap, first.getTo(), spaceId);
        }
        writer.append(SocialNotificationUtils.getMessageByIds(receiverMap, templateContext, "space"));
      } catch (IOException e) {
        ctx.setException(e);
        return false;
      }

      return true;
    }

  };

  private class JoinedSpaceByInvitationLinkTemplateBuilder extends AbstractTemplateBuilder {

  @Override
  protected MessageInfo makeMessage(NotificationContext notificationContext) {
    NotificationInfo notificationInfo = notificationContext.getNotificationInfo();
    String pluginId = notificationInfo.getKey().getId();
    HTMLEntityEncoder encoder = HTMLEntityEncoder.getInstance();
    String invitedUserDisplayName = notificationInfo.getValueOwnerParameter(NotificationUtils.INVITED_USER.getKey());
    String inviterId = notificationInfo.getValueOwnerParameter(NotificationUtils.INVITER_ID.getKey());
    String spaceId = notificationInfo.getValueOwnerParameter(NotificationUtils.SPACE_ID.getKey());
    String spaceDisplayName = notificationInfo.getValueOwnerParameter(NotificationUtils.SPACE_DISPLAY_NAME.getKey());
    String spaceAvatarUrl = notificationInfo.getValueOwnerParameter(NotificationUtils.SPACE_AVATAR_URL.getKey());
    Identity inviterIdentity = Utils.getIdentityManager().getOrCreateUserIdentity(inviterId);
    String language = getLanguage(notificationInfo);
    TemplateContext templateContext = TemplateContext.newChannelInstance(getChannelKey(), pluginId, language);
    SocialNotificationUtils.addFooterAndFirstName(notificationInfo.getTo(), templateContext);


    templateContext.put("INVITED_USER_DISPLAY_NAME", encoder.encode(invitedUserDisplayName));
    templateContext.put("SPACE_DISPLAY_NAME", encoder.encode(spaceDisplayName));
    templateContext.put("SPACE_MEMBERS_URL", "/portal/s/" + encoder.encode(spaceId) + "/members");
    templateContext.put("SPACE_AVATAR_URL", encoder.encode(spaceAvatarUrl));
    templateContext.put("USER", Utils.addExternalFlag(inviterIdentity));
    templateContext.put("AVATAR", CommonsUtils.getCurrentDomain() + spaceAvatarUrl);

    String subject = TemplateUtils.processSubject(templateContext);
    String body = TemplateUtils.processGroovy(templateContext);
    notificationContext.setException(templateContext.getException());
    MessageInfo messageInfo = new MessageInfo();
    return messageInfo.subject(subject).body(body).end();
  }

    @Override
    protected boolean makeDigest(NotificationContext notificationContext, Writer writer) {
      return false;
    }
  }

  protected ExoSocialActivity getI18N(ExoSocialActivity activity,Locale locale) {

    I18NActivityProcessor i18NActivityProcessor =(I18NActivityProcessor) PortalContainer.getInstance().getComponentInstanceOfType(I18NActivityProcessor.class);
    if (activity.getTitleId() != null) {
      activity = i18NActivityProcessor.process(activity, locale);
    }
    return activity;
  }

  public MailTemplateProvider(InitParams initParams) {
    super(initParams);
    this.templateBuilders.put(PluginKey.key(ActivityCommentPlugin.ID), comment);
    this.templateBuilders.put(PluginKey.key(ActivityCommentWatchPlugin.ID), comment);
    this.templateBuilders.put(PluginKey.key(EditCommentPlugin.ID), editComment);
    this.templateBuilders.put(PluginKey.key(EditActivityPlugin.ID), editActivity);
    this.templateBuilders.put(PluginKey.key(ActivityReplyToCommentPlugin.ID), replyToComment);
    this.templateBuilders.put(PluginKey.key(ActivityMentionPlugin.ID), mention);
    this.templateBuilders.put(PluginKey.key(LikePlugin.ID), like);
    this.templateBuilders.put(PluginKey.key(LikeCommentPlugin.ID), likeComment);
    this.templateBuilders.put(PluginKey.key(NewUserPlugin.ID), newUser);
    this.templateBuilders.put(PluginKey.key(PostActivityPlugin.ID), postActivity);
    this.templateBuilders.put(PluginKey.key(PostActivitySpaceStreamPlugin.ID), postActivitySpace);
    this.templateBuilders.put(PluginKey.key(SharedActivitySpaceStreamPlugin.ID), shareActivitySpace);
    this.templateBuilders.put(PluginKey.key(RelationshipReceivedRequestPlugin.ID), relationshipReceived);
    this.templateBuilders.put(PluginKey.key(RequestJoinSpacePlugin.ID), requestJoinSpace);
    this.templateBuilders.put(PluginKey.key(SpaceInvitationPlugin.ID), spaceInvitation);
    this.templateBuilders.put(PluginKey.key(JoinedSpaceByInvitationLinkPlugin.ID), new JoinedSpaceByInvitationLinkTemplateBuilder());
  }

  private String getActivityTitle(ExoSocialActivity activity, String language) {
    return MentionUtils.substituteRoleWithLocale(Utils.getActivityManager().getActivityTitle(activity),
                                                 Locale.forLanguageTag(language));
  }

  private String processBody(String message, String language) {
    message = MentionUtils.substituteRoleWithLocale(message, Locale.forLanguageTag(language));
    return org.exoplatform.commons.notification.NotificationUtils.processLinkTitle(message);
  }

}
