/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.gallery.service;

import org.silverpeas.components.gallery.model.GalleryRuntimeException;
import org.silverpeas.components.gallery.model.Media;
import org.silverpeas.core.admin.user.model.UserDetail;
import org.silverpeas.core.notification.NotificationException;
import org.silverpeas.core.notification.user.client.NotificationMetaData;
import org.silverpeas.core.notification.user.client.NotificationSender;
import org.silverpeas.core.notification.user.client.UserRecipient;
import org.silverpeas.core.scheduler.Scheduler;
import org.silverpeas.core.scheduler.SchedulerEvent;
import org.silverpeas.core.scheduler.SchedulerEventListener;
import org.silverpeas.core.scheduler.SchedulerProvider;
import org.silverpeas.core.scheduler.trigger.JobTrigger;
import org.silverpeas.core.ui.DisplayI18NHelper;
import org.silverpeas.core.util.Link;
import org.silverpeas.kernel.bundle.LocalizationBundle;
import org.silverpeas.kernel.bundle.ResourceLocator;
import org.silverpeas.kernel.bundle.SettingBundle;
import org.silverpeas.core.util.URLUtil;
import org.silverpeas.kernel.logging.SilverLogger;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static org.silverpeas.components.gallery.service.MediaServiceProvider.getMediaService;
import static org.silverpeas.core.admin.service.OrganizationControllerProvider.getOrganisationController;
import static org.silverpeas.core.notification.user.client.NotificationParameters.PRIORITY_NORMAL;

public class ScheduledAlertUser implements SchedulerEventListener {

  public static final String GALLERYENGINE_JOB_NAME = "GalleryEngineJob";

  public void initialize() {
    try {
      SettingBundle resources =
          ResourceLocator.getSettingBundle("org.silverpeas.gallery.settings.gallerySettings");
      String cron = resources.getString("cronScheduledAlertUser");
      Scheduler scheduler = SchedulerProvider.getVolatileScheduler();
      scheduler.unscheduleJob(GALLERYENGINE_JOB_NAME);
      JobTrigger trigger = JobTrigger.triggerAt(cron);
      scheduler.scheduleJob(GALLERYENGINE_JOB_NAME, trigger, this);
    } catch (Exception e) {
      SilverLogger.getLogger(this).error(
          "can not initialize successfully the batch in charge of alerting administrators about " +
              "the end of visibility of media", e);
    }
  }

  public void doScheduledAlertUser() {
    try {
      // Finding media for which the visibility will soon end
      SettingBundle resources =
          ResourceLocator.getSettingBundle("org.silverpeas.gallery.settings.gallerySettings");
      int nbDays = resources.getInteger("nbDaysForAlertUser");
      Collection<Media> mediaList = getMediaService().getAllMediaThatWillBeNotVisible(nbDays);

      String currentInstanceId = null;
      LocalizedContent messageContent = new LocalizedContent();
      for (Media media : mediaList) {
        if (!media.getInstanceId().equals(currentInstanceId)) {
          if (currentInstanceId != null) {
            // Sending the notification
            createMessage(messageContent, currentInstanceId);
            messageContent = new LocalizedContent();
          }
          currentInstanceId = media.getInstanceId();
        }
        // Adding the media header information
        messageContent.appendFromBundleKey("gallery.notifName").append(" : ")
            .append(media.getName()).append("\n");
      }

      // Finally send the last message
      if (currentInstanceId != null) {
        createMessage(messageContent, currentInstanceId);
      }
    } catch (Exception e) {
      throw new GalleryRuntimeException(e);
    }
  }

  private void createMessage(LocalizedContent localizedContent, String componentInstanceId) {
    UserDetail[] admins = getOrganisationController().getUsers(componentInstanceId, "admin");
    if (admins == null || admins.length == 0) {
      return;
    }

    NotificationMetaData notificationMetaData = new NotificationMetaData(PRIORITY_NORMAL, "", "");

    // Preparing the notification content
    localizedContent.append("\n").appendFromBundleKey("gallery.notifUserInfo").append("\n\n");
    mergeLocalizedContentIntoNotificationMetaData(componentInstanceId, localizedContent,
        notificationMetaData);
    notificationMetaData.setComponentId(componentInstanceId);

    // Sending the notification to the component instance administrators
    for (UserDetail admin : admins) {
      notificationMetaData.addUserRecipient(new UserRecipient(admin));
    }
    NotificationSender notifSender = new NotificationSender(componentInstanceId);
    try {
      notifSender.notifyUser(notificationMetaData);
    } catch (NotificationException e) {
      SilverLogger.getLogger(this)
          .error("can not send the notification message about media which will be no more visible",
              e);
    }
  }

  private void mergeLocalizedContentIntoNotificationMetaData(final String componentInstanceId,
      LocalizedContent localizedContent, NotificationMetaData notificationMetaData) {
    String nameInstance =
        getOrganisationController().getComponentInst(componentInstanceId).getLabel();
    String url = URLUtil.getComponentInstanceURL(componentInstanceId) + "Main";
    for (String language : DisplayI18NHelper.getLanguages()) {
      LocalizationBundle bundle = LocalizedContent.getBundle(language);
      String subject = bundle.getString("gallery.notifSubject");
      String body = MessageFormat
          .format("{0} <b>{1}</b>\n\n{2}", bundle.getString("gallery.notifTitle"), nameInstance,
              localizedContent.get(language));
      notificationMetaData.addLanguage(language, subject, body);
      Link link = new Link(url, nameInstance);
      notificationMetaData.setLink(link, language);
    }
  }

  @Override
  public void triggerFired(SchedulerEvent anEvent) {
    doScheduledAlertUser();
  }

  @Override
  public void jobSucceeded(SchedulerEvent anEvent) {
    // nothing to do
  }

  @Override
  public void jobFailed(SchedulerEvent anEvent) {
    SilverLogger.getLogger(this).error(
        "The job '" + anEvent.getJobExecutionContext().getJobName() + "' was not successfull",
        anEvent.getJobThrowable());
  }

  private static class LocalizedContent {
    private Map<String, StringBuilder> localizedContents = new HashMap<>();

    public LocalizedContent appendFromBundleKey(String key) {
      for (String language : DisplayI18NHelper.getLanguages()) {
        append(localizedContents, language, getBundle(language).getString(key));
      }
      return this;
    }

    public LocalizedContent append(String content) {
      for (String language : DisplayI18NHelper.getLanguages()) {
        append(localizedContents, language, content);
      }
      return this;
    }

    static LocalizationBundle getBundle(String language) {
      return ResourceLocator
          .getLocalizationBundle("org.silverpeas.gallery.multilang.galleryBundle", language);
    }

    private static void append(Map<String, StringBuilder> container, String language,
        String message) {
      StringBuilder sb =  container.computeIfAbsent(language, l -> new StringBuilder());
      sb.append(message);
    }

    public String get(final String language) {
      StringBuilder sb = localizedContents.get(language);
      return sb != null ? sb.toString() : "";
    }
  }
}
