package com.atlassian.stash.internal.notification.custom.pull;

import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.event.api.EventListener;
import com.atlassian.bitbucket.i18n.I18nKey;
import com.atlassian.stash.internal.notification.handlers.NotificationMailer;
import com.atlassian.stash.internal.notification.handlers.WatchableMailThreadHeaderTransformer;
import com.atlassian.stash.internal.notification.pull.PullRequestNotificationUtils;
import com.atlassian.stash.internal.notification.pull.activity.CustomData;
import com.atlassian.stash.internal.notification.pull.activity.PullRequestBatchId;
import com.atlassian.stash.internal.notification.pull.activity.PullRequestBatchNotificationDao;
import com.atlassian.stash.internal.notification.usersettings.SendMode;
import com.atlassian.stash.internal.notification.usersettings.UserNotificationSettings;
import com.atlassian.stash.internal.notification.usersettings.UserNotificationSettingsService;
import com.atlassian.bitbucket.notification.custom.pull.CustomPullRequestNotificationEvent;
import com.atlassian.bitbucket.notification.custom.pull.CustomNotificationSection;
import com.atlassian.bitbucket.notification.custom.pull.CustomPullRequestNotificationRenderer;
import com.atlassian.bitbucket.notification.custom.pull.CustomNotificationData;
import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.*;

import static com.atlassian.stash.internal.notification.util.MapUtils.createMap;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Listener for custom pull request notifications.
 *
 * @see CustomPullRequestNotificationEvent
 */
public class CustomPullRequestNotificationListener {

    private static final Logger log = LoggerFactory.getLogger(CustomPullRequestNotificationListener.class);

    private static final String TEMPLATE = "bitbucket.internal.notification.email.pullRequest.customSection";

    private final CustomPullRequestNotificationRendererAccessor rendererAccessor;
    private final PullRequestBatchNotificationDao notificationDao;
    private final NotificationMailer notificationMailer;
    private final SecurityService securityService;
    private final UserNotificationSettingsService settingsService;

    public CustomPullRequestNotificationListener(@Nonnull CustomPullRequestNotificationRendererAccessor rendererAccessor,
                                                 @Nonnull PullRequestBatchNotificationDao notificationDao,
                                                 @Nonnull NotificationMailer notificationMailer,
                                                 @Nonnull SecurityService securityService,
                                                 @Nonnull UserNotificationSettingsService settingsService) {
        this.rendererAccessor = checkNotNull(rendererAccessor, "rendererAccessor");
        this.notificationDao = checkNotNull(notificationDao, "notificationDao");
        this.notificationMailer = checkNotNull(notificationMailer, "notificationMailer");
        this.securityService = checkNotNull(securityService, "securityService");
        this.settingsService = checkNotNull(settingsService, "settingsService");
    }

    @EventListener
    public void onCustomEvent(CustomPullRequestNotificationEvent customEvent) {
        CustomPullRequestNotificationRenderer renderer = rendererAccessor.getRenderer(customEvent.getRendererId());
        if (renderer == null) {
            log.warn("No renderer with ID '{}' found for the custom notification event '{}', dropping event",
                    customEvent.getRendererId(), customEvent);
            return;
        }

        Multimap<SendMode, ApplicationUser> recipients = HashMultimap.create(2, 4); // 2 send mode, 4 notification buffer
        for (ApplicationUser recipient : customEvent.getRecipients()) {
            UserNotificationSettings settings = securityService.impersonating(recipient, "notification handling").call(
                    () -> settingsService.getSettingsForUser(recipient));
            recipients.put(settings.getSendMode(), recipient);
        }

        CustomNotificationData data = customEvent.getData();
        PullRequest pullRequest = customEvent.getPullRequest();

        Collection<ApplicationUser> immediateRecipients = recipients.get(SendMode.IMMEDIATE);
        if (!immediateRecipients.isEmpty()) {
            Map<ApplicationUser, Collection<CustomNotificationData>> userToData = createMap(immediateRecipients, Collections.singleton(data));
            sendNotifications(customEvent, renderer.render(pullRequest, userToData));
        }

        Collection<ApplicationUser> batchedRecipients = recipients.get(SendMode.BATCHED);
        if (!batchedRecipients.isEmpty()) {
            Map<ApplicationUser, CustomNotificationData> userToData = createMap(batchedRecipients, data);
            queueNotifications(customEvent, userToData);
        }
    }

    private void sendNotifications(CustomPullRequestNotificationEvent notificationEvent,
                                   Map<ApplicationUser, Collection<CustomNotificationSection>> customSections) {
        PullRequest pullRequest = notificationEvent.getPullRequest();
        I18nKey subjectKey = PullRequestNotificationUtils.createSubjectKey(pullRequest);

        for (Map.Entry<ApplicationUser, Collection<CustomNotificationSection>> entry : customSections.entrySet()) {
            ApplicationUser user = entry.getKey();
            for (CustomNotificationSection section : entry.getValue()) {
                String content = section.getContent();
                notificationMailer.renderAndSend(notificationEvent, Collections.singleton(user), subjectKey, TEMPLATE,
                        ImmutableMap.of(
                                "pullRequest", pullRequest,
                                "moduleKey", NotificationMailer.EMAIL_STYLES_MODULE,
                                "batchable", true,
                                "content", content),
                        new WatchableMailThreadHeaderTransformer(pullRequest));
            }
        }
    }

    private void queueNotifications(CustomPullRequestNotificationEvent notificationEvent,
                                    Map<ApplicationUser, CustomNotificationData> userToData) {
        PullRequest pullRequest = notificationEvent.getPullRequest();
        int repositoryId = pullRequest.getToRef().getRepository().getId();
        PullRequestBatchId batchId = PullRequestBatchId.of(repositoryId, pullRequest.getId());

        for (Map.Entry<ApplicationUser, CustomNotificationData> entry : userToData.entrySet()) {
            ApplicationUser user = entry.getKey();
            CustomNotificationData data = entry.getValue();

            CustomData customData = new CustomData(notificationEvent.getRendererId(), data.getData());
            notificationDao.queue(Collections.singleton(user), notificationEvent.getDate(), batchId, customData);
        }
    }

}
