"use strict";
/*
 * Copyright © 2020 Atomist, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.truncateCommitMessage = exports.notifyGoalCompletionListener = exports.defaultNotificationFactory = exports.defaultDestinationFactory = exports.notificationSupport = void 0;
const string_1 = require("@atomist/automation-client/lib/internal/util/string");
const MessageClient_1 = require("@atomist/automation-client/lib/spi/message/MessageClient");
const slack_messages_1 = require("@atomist/slack-messages");
const _ = require("lodash");
const extensionPack_1 = require("../../api-helper/misc/extensionPack");
const messages_1 = require("../../api-helper/misc/slack/messages");
const buttons_1 = require("../../api/command/support/buttons");
const array_1 = require("../../core/util/misc/array");
const types_1 = require("../../typings/types");
const updateGoal_1 = require("../goal-state/updateGoal");
/**
 * Extension pack to send notifications on certain conditions.
 * Recipients and notification messages can be customized by providing options
 * with DestinationFactory and NotificationFactory.
 * @param options
 */
function notificationSupport(options = {}) {
    return Object.assign(Object.assign({}, extensionPack_1.metadata("notification")), { configure: sdm => {
            const updateGoalCommand = updateGoal_1.updateGoalStateCommand();
            updateGoalCommand.name = `${updateGoalCommand.name}ForNotifications`;
            sdm.addCommand(updateGoalCommand);
            const optsToUse = Object.assign({ destination: defaultDestinationFactory, notification: defaultNotificationFactory(updateGoalCommand) }, options);
            sdm.addGoalCompletionListener(notifyGoalCompletionListener(optsToUse));
        } });
}
exports.notificationSupport = notificationSupport;
/**
 * Default DestinationFactory that would send every commit author a direct message in Slack / MS Teams.
 */
async function defaultDestinationFactory(goal) {
    if (goal.state === types_1.SdmGoalState.failure) {
        return _.uniqBy(goal.push.commits.map(c => _.get(c, "author.person.chatId"))
            .filter(c => !!c), r => `${r.chatTeam.id}.${r.screenName}`)
            .map(r => MessageClient_1.addressSlackUsers(r.chatTeam.id, r.screenName));
    }
    return undefined;
}
exports.defaultDestinationFactory = defaultDestinationFactory;
/**
 * Default NotificationFactory that sends messages with restart, start and approve buttons where appropriate.
 */
function defaultNotificationFactory(updateGoalCommand) {
    return async (gi) => {
        const { completedGoal, context } = gi;
        const goalSetId = completedGoal.goalSetId;
        const uniqueName = completedGoal.uniqueName;
        const msgId = string_1.guid();
        let state;
        let suffix;
        let msg;
        switch (completedGoal.state) {
            case types_1.SdmGoalState.failure:
                state = "has failed";
                suffix = "Failed";
                msg = messages_1.slackErrorMessage("", "", context, {
                    actions: completedGoal.retryFeasible ? [
                        buttons_1.actionableButton({ text: "Restart" }, updateGoalCommand, {
                            goalSetId,
                            uniqueName,
                            msgId,
                            state: types_1.SdmGoalState.requested,
                        })
                    ] : [],
                });
                break;
            case types_1.SdmGoalState.waiting_for_approval:
                state = "is waiting for approval";
                suffix = "Awaiting Approval";
                msg = messages_1.slackInfoMessage("", "", {
                    actions: [buttons_1.actionableButton({ text: "Approve" }, updateGoalCommand, {
                            goalSetId,
                            uniqueName,
                            msgId,
                            state: types_1.SdmGoalState.approved,
                        })],
                });
                break;
            case types_1.SdmGoalState.waiting_for_pre_approval:
                state = "is waiting to start";
                suffix = "Awaiting Start";
                msg = messages_1.slackInfoMessage("", "", {
                    actions: [buttons_1.actionableButton({ text: "Start" }, updateGoalCommand, {
                            goalSetId,
                            uniqueName,
                            msgId,
                            state: types_1.SdmGoalState.pre_approved,
                        })],
                });
                break;
            case types_1.SdmGoalState.stopped:
                state = "has stopped";
                suffix = "Stopped";
                msg = messages_1.slackInfoMessage("", "");
                break;
            default:
                return undefined;
        }
        const author = `Goal ${suffix}`;
        const commitMsg = truncateCommitMessage(completedGoal.push.after.message);
        const text = `Goal ${slack_messages_1.italic(completedGoal.url ? slack_messages_1.url(completedGoal.url, completedGoal.name) : completedGoal.name)} on ${slack_messages_1.url(completedGoal.push.after.url, slack_messages_1.codeLine(completedGoal.sha.slice(0, 7)))} ${slack_messages_1.italic(commitMsg)} of ${slack_messages_1.bold(`${slack_messages_1.url(completedGoal.push.repo.url, `${completedGoal.repo.owner}/${completedGoal.repo.name}/${completedGoal.branch}`)}`)} ${state}.`;
        const channels = _.get(completedGoal, "push.repo.channels") || [];
        const channelLink = channels.filter(c => !!c.channelId).map(c => slack_messages_1.channel(c.channelId)).join(" \u00B7 ");
        const link = `https://app.atomist.com/workspace/${context.workspaceId}/goalset/${completedGoal.goalSetId}`;
        msg.attachments[0] = Object.assign(Object.assign({}, msg.attachments[0]), { author_name: author, text, footer: `${messages_1.slackFooter()} \u00B7 ${slack_messages_1.url(link, completedGoal.goalSetId.slice(0, 7))} \u00B7 ${channelLink}` });
        return { message: msg, options: { id: msgId } };
    };
}
exports.defaultNotificationFactory = defaultNotificationFactory;
/**
 * GoalCompletionListener that delegates to the NotificationFactory and DestinationFactory to
 * create notifications and send them out.
 */
function notifyGoalCompletionListener(options) {
    return async (gi) => {
        const { completedGoal, context } = gi;
        const destinations = [];
        for (const destinationFactory of array_1.toArray(options.destination || [])) {
            const newDestinations = await destinationFactory(completedGoal, gi);
            if (!!newDestinations) {
                destinations.push(...array_1.toArray(newDestinations));
            }
        }
        if (destinations.length > 0) {
            const msg = await options.notification(gi);
            for (const destination of destinations) {
                await context.messageClient.send(msg.message, destination, msg.options);
            }
        }
    };
}
exports.notifyGoalCompletionListener = notifyGoalCompletionListener;
function truncateCommitMessage(message) {
    const title = (message || "").split("\n")[0];
    const escapedTitle = slack_messages_1.escape(title);
    if (escapedTitle.length <= 50) {
        return escapedTitle;
    }
    const splitRegExp = /(&(?:[gl]t|amp);|<.*?\||>)/;
    const titleParts = escapedTitle.split(splitRegExp);
    let truncatedTitle = "";
    let addNext = 1;
    let i;
    for (i = 0; i < titleParts.length; i++) {
        let newTitle = truncatedTitle;
        if (i % 2 === 0) {
            newTitle += titleParts[i];
        }
        else if (/^&(?:[gl]t|amp);$/.test(titleParts[i])) {
            newTitle += "&";
        }
        else if (/^<.*\|$/.test(titleParts[i])) {
            addNext = 2;
            continue;
        }
        else if (titleParts[i] === ">") {
            addNext = 1;
            continue;
        }
        if (newTitle.length > 50) {
            const l = 50 - newTitle.length;
            titleParts[i] = titleParts[i].slice(0, l) + "...";
            break;
        }
        truncatedTitle = newTitle;
    }
    return titleParts.slice(0, i + addNext).join("");
}
exports.truncateCommitMessage = truncateCommitMessage;
//# sourceMappingURL=notification.js.map