"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SendEmailPerformer = exports.Liquid = exports.EXTERNAL_ID_KEY = void 0;
const actions_shared_1 = require("@segment/actions-shared");
const liquidjs_1 = require("liquidjs");
const actions_core_1 = require("@segment/actions-core");
const apiLookups_1 = require("../previewApiLookup/apiLookups");
const insertEmailPreviewText_1 = require("./insertEmailPreviewText");
const cheerio_1 = __importDefault(require("cheerio"));
const isRestrictedDomain_1 = require("./isRestrictedDomain");
const versioning_info_1 = require("../versioning-info");
exports.EXTERNAL_ID_KEY = 'email';
exports.Liquid = new liquidjs_1.Liquid();
class SendEmailPerformer extends actions_shared_1.MessageSendPerformer {
    getIntegrationStatsName() {
        return 'actions_personas_messaging_sendgrid';
    }
    getChannelType() {
        return 'email';
    }
    getDefaultSettingsRegion() {
        return 'us-west-2';
    }
    isSupportedExternalId(externalId) {
        return externalId.type === 'email';
    }
    isExternalIdSubscribed(extId) {
        const bypass_subscription = this.payload.byPassSubscription !== undefined && this.payload.byPassSubscription;
        if (bypass_subscription) {
            this.currentOperation?.logs.push('Bypassing subscription');
            this.currentOperation?.tags.push('bypass_subscription:' + true);
            return true;
        }
        const isSubscribed = super.isExternalIdSubscribed(extId);
        if (!isSubscribed)
            return isSubscribed;
        if (!this.payload.groupId)
            return isSubscribed;
        const recepientGroup = extId.groups?.find((g) => g.id === this.payload.groupId);
        if (!recepientGroup) {
            this.statsClient.incr('group_notfound', 1);
            return false;
        }
        if (!recepientGroup.isSubscribed) {
            this.statsClient.incr('group_notsubscribed', 1);
            return false;
        }
        return true;
    }
    getRecepients() {
        if (this.payload.toEmail) {
            const externalIdContext = this.payload?.externalIds && this.payload?.externalIds[0];
            return [
                {
                    id: this.payload.toEmail,
                    type: 'email',
                    subscriptionStatus: externalIdContext?.subscriptionStatus,
                    unsubscribeLink: externalIdContext?.unsubscribeLink,
                    preferencesLink: externalIdContext?.preferencesLink,
                    groups: externalIdContext?.groups
                }
            ];
        }
        const res = super.getRecepients();
        if (res.length > 0)
            return [res[0]];
        return res;
    }
    async parseTemplating(content, liquidData, contentType) {
        const traits = liquidData.profile.traits ? { ...liquidData.profile.traits } : liquidData.profile.traits;
        if (traits) {
            for (const trait of Object.keys(traits)) {
                if (traits && traits[trait] && (traits[trait] === '<nil>' || traits[trait].toString().trim() === '')) {
                    traits[trait] = '';
                }
            }
        }
        const parsedContent = content == null
            ? content
            : await exports.Liquid.parseAndRender(content, { ...liquidData, profile: { ...liquidData.profile, traits } });
        this.logOnError(() => 'Content type: ' + contentType);
        return parsedContent;
    }
    async sendToRecepient(emailProfile) {
        const traits = await this.getProfileTraits();
        const profile = {
            email: emailProfile?.id,
            traits
        };
        const toEmail = profile.email;
        if (!toEmail) {
            return;
        }
        if ((0, isRestrictedDomain_1.isRestrictedDomain)(toEmail)) {
            this.tags.push('reason:restricted_domain');
            throw new actions_core_1.IntegrationError('Emails with gmailx.com, yahoox.com, aolx.com, and hotmailx.com domains are blocked.', 'Invalid input', 400);
        }
        const bcc = JSON.parse(this.payload.bcc ?? '[]');
        const [parsedFromEmail, parsedFromName, parsedFromReplyToEmail, parsedFromReplyToName, parsedSubject] = await Promise.all([
            this.parseTemplating(this.payload.fromEmail, { profile }, 'FromEmail'),
            this.parseTemplating(this.payload.fromName, { profile }, 'FromName'),
            this.parseTemplating(this.payload.replyToEmail, { profile }, 'ReplyToEmail'),
            this.parseTemplating(this.payload.replyToName, { profile }, 'ReplyToName'),
            this.parseTemplating(this.payload.subject, { profile }, 'Subject')
        ]);
        let apiLookupData = {};
        if (this.isFeatureActive(apiLookups_1.FLAGON_NAME_DATA_FEEDS)) {
            try {
                apiLookupData = await this.performApiLookups(this.payload.apiLookups, profile);
            }
            catch (error) {
                this.tags.push('reason:data_feed_failure');
                throw error;
            }
        }
        const parsedBodyHtml = await this.getBodyHtml(profile, apiLookupData, emailProfile);
        const mailContentSubscriptionHonored = {
            personalizations: [
                {
                    to: [
                        {
                            email: toEmail
                        }
                    ],
                    bcc: bcc.length > 0 ? bcc : undefined,
                    custom_args: {
                        ...this.payload.customArgs,
                        source_id: this.settings.sourceId,
                        space_id: this.settings.spaceId,
                        user_id: this.payload.userId ?? undefined,
                        message_id: this.getMessageId(),
                        __segment_internal_external_id_key__: exports.EXTERNAL_ID_KEY,
                        __segment_internal_external_id_value__: profile[exports.EXTERNAL_ID_KEY]
                    }
                }
            ],
            from: {
                email: parsedFromEmail,
                name: parsedFromName
            },
            reply_to: {
                email: parsedFromReplyToEmail,
                name: parsedFromReplyToName
            },
            subject: parsedSubject,
            content: [
                {
                    type: 'text/html',
                    value: parsedBodyHtml
                }
            ],
            tracking_settings: {
                subscription_tracking: {
                    enable: true,
                    substitution_tag: '[unsubscribe]'
                }
            }
        };
        let mailContent;
        let unsubscribeLink;
        if (this.payload.groupId) {
            const group = emailProfile.groups?.find((grp) => grp.id === this.payload.groupId);
            unsubscribeLink = group?.groupUnsubscribeLink ?? '';
        }
        else {
            unsubscribeLink = emailProfile?.unsubscribeLink;
        }
        if (unsubscribeLink) {
            mailContent = {
                ...mailContentSubscriptionHonored,
                headers: {
                    'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
                    'List-Unsubscribe': '<' + unsubscribeLink + '>'
                }
            };
            mailContent.tracking_settings.subscription_tracking.enable = false;
            this.statsClient?.incr('request.list_unsubscribe__header_added', 1);
        }
        else {
            mailContent = mailContentSubscriptionHonored;
        }
        if (this.payload.byPassSubscription) {
            mailContent = {
                ...mailContent,
                mail_settings: {
                    bypass_list_management: {
                        enable: true
                    }
                }
            };
            this.statsClient?.incr('request.by_pass_subscription', 1);
        }
        else {
            this.statsClient?.incr('request.dont_pass_subscription', 1);
        }
        if (this.payload.ipPool) {
            mailContent = {
                ...mailContent,
                ip_pool_name: this.payload.ipPool
            };
            this.statsClient?.incr('request.ip_pool_name_provided', 1);
        }
        else {
            this.statsClient?.incr('request.ip_pool_name_not_provided', 1);
        }
        const req = {
            method: 'post',
            headers: {
                authorization: `Bearer ${this.settings.sendGridApiKey}`,
                'x-nef-fp': process.env.SENDGRID_BYPASS_IP_BLOCK_HEADER_SECRET ?? ''
            },
            json: mailContent
        };
        const response = await this.request(`https://api.sendgrid.com/${versioning_info_1.ENGAGE_MESSAGING_SENDGRID_API_VERSION}/mail/send`, req);
        if (this.payload?.eventOccurredTS != undefined) {
            this.statsClient?.histogram('eventDeliveryTS', Date.now() - new Date(this.payload?.eventOccurredTS).getTime(), this.tags);
        }
        return response;
    }
    async getBodyTemplateFromS3(bodyUrl) {
        if (this.isFeatureActive('engage-cache-email-template'))
            return await this.getOrAddCache(`email-template:${bodyUrl}`, async () => (await this.request(bodyUrl, { method: 'GET', skipResponseCloning: true })).content, {
                cacheGroup: 'getBodyTemplateFromS3',
                expiryInSeconds: 4 * 60 * 60,
                lockOptions: {
                    lockMaxTimeMs: (this.isLockExpirationExtended() ? 30 : 10) * 60000,
                    acquireLockMaxWaitTimeMs: 30000,
                    acquireLockRetryIntervalMs: 1000
                }
            });
        else
            return (await this.request(bodyUrl, { method: 'GET', skipResponseCloning: true })).content;
    }
    async getBodyHtml(profile, apiLookupData, emailProfile) {
        let parsedBodyHtml;
        if (this.payload.bodyUrl && this.settings.unlayerApiKey) {
            const body = await this.getBodyTemplateFromS3(this.payload.bodyUrl);
            const bodyHtml = this.payload.bodyType === 'html' ? body : await this.generateEmailHtml(body);
            parsedBodyHtml = await this.parseTemplating(bodyHtml, { profile, [apiLookups_1.apiLookupLiquidKey]: apiLookupData }, 'Body');
        }
        else {
            parsedBodyHtml = await this.parseTemplating(this.payload.bodyHtml ?? '', { profile, [apiLookups_1.apiLookupLiquidKey]: apiLookupData }, 'Body HTML');
        }
        if (this.payload.previewText) {
            try {
                const parsedPreviewText = await this.parseTemplating(this.payload.previewText, { profile }, 'Preview text');
                parsedBodyHtml = (0, insertEmailPreviewText_1.insertEmailPreviewText)(parsedBodyHtml, parsedPreviewText);
            }
            catch (ex) {
                this.logger?.error('Error inserting preview text, using original html', {
                    ex
                });
                this.statsClient.incr('insert_preview_fail', 1);
            }
        }
        parsedBodyHtml = this.insertUnsubscribeLinks(parsedBodyHtml, emailProfile);
        return parsedBodyHtml;
    }
    async generateEmailHtml(design) {
        this.statsClient.incr('unlayer_request', 1);
        const response = await this.request('https://api.unlayer.com/v2/export/html', {
            method: 'POST',
            headers: {
                authorization: `Basic ${Buffer.from(`${this.settings.unlayerApiKey}:`).toString('base64')}`,
                'content-type': 'application/json'
            },
            body: JSON.stringify({
                displayMode: 'email',
                design: JSON.parse(design)
            })
        });
        const body = await response.json();
        return body.data.html;
    }
    async performApiLookups(apiLookups, profile) {
        if (!apiLookups) {
            return {};
        }
        const request = this.request.bind(this);
        const data = await Promise.all(apiLookups.map(async (apiLookup) => {
            const data = await (0, apiLookups_1.performApiLookup)(request, apiLookup, profile, this.statsClient.statsClient, this.tags, this.settings, this.logger.loggerClient, this.engageDestinationCache);
            return { name: apiLookup.name, data };
        }));
        return data.reduce((acc, { name, data }) => {
            acc[name] = data;
            return acc;
        }, {});
    }
    validateLinkAndLog(link) {
        let workspaceId = this.payload.customArgs && this.payload.customArgs['workspace_id'];
        let audienceId = this.payload.customArgs &&
            (this.payload.customArgs['audience_id'] || this.payload.customArgs['__segment_internal_audience_id__']);
        workspaceId = JSON.stringify(workspaceId);
        audienceId = JSON.stringify(audienceId);
        this.logger.info(`Validating the link: ${link} ${workspaceId} ${audienceId}`);
        const parsedLink = new URL(link);
        const checkParam = (paramName) => {
            const paramValue = parsedLink.searchParams.get(paramName);
            if (!paramValue || paramValue === '') {
                this.logger.error(`${paramName} is missing: ${link} ${workspaceId} ${audienceId}`);
                this.statsClient.incr('missing_query_param', 1, [`param:${paramName}`, `audienceId:${audienceId}`]);
            }
        };
        const requiredParams = ['contactId', 'data', 'code', 'spaceId', 'workspaceId', 'messageId', 'user-agent'];
        requiredParams.forEach((param) => checkParam(param));
    }
    insertUnsubscribeLinks(html, emailProfile) {
        const spaceId = this.settings.spaceId;
        const groupId = this.payload.groupId;
        const globalUnsubscribeLink = emailProfile?.unsubscribeLink;
        const preferencesLink = emailProfile?.preferencesLink;
        const unsubscribeLinkRef = 'a[href*="[upa_unsubscribe_link]"]';
        const preferencesLinkRef = 'a[href*="[upa_preferences_link]"]';
        const sendgridUnsubscribeLinkRef = 'a[href*="[unsubscribe]"]';
        const sendgridUnsubscribeLinkTag = '[unsubscribe]';
        const $ = cheerio_1.default.load(html);
        const _this = this;
        let hasSendgridSubstitutionTag = false;
        $(sendgridUnsubscribeLinkRef).each(function () {
            emailProfile.unsubscribeLink = '';
            emailProfile.preferencesLink = '';
            if (groupId) {
                const group = emailProfile.groups?.find((grp) => grp.id === groupId);
                if (group) {
                    group.groupUnsubscribeLink = '';
                }
            }
            hasSendgridSubstitutionTag = true;
        });
        if (hasSendgridSubstitutionTag) {
            return $.html();
        }
        if ($(unsubscribeLinkRef).length === 0) {
            _this.logger.info(`Unsubscribe tag is missing`);
            emailProfile.unsubscribeLink = '';
        }
        if ($(preferencesLinkRef).length === 0) {
            _this.logger.info(`Preferences tag missing`);
            emailProfile.preferencesLink = '';
        }
        if (groupId) {
            const group = emailProfile.groups?.find((grp) => grp.id === groupId);
            const groupUnsubscribeLink = group?.groupUnsubscribeLink;
            $(unsubscribeLinkRef).each(function () {
                if (!groupUnsubscribeLink) {
                    _this.logger.info(`Group Unsubscribe link missing`);
                    _this.statsClient.incr('group_unsubscribe_link_missing', 1);
                    $(this).attr('href', sendgridUnsubscribeLinkTag);
                }
                else {
                    _this.validateLinkAndLog(groupUnsubscribeLink);
                    $(this).removeAttr('href');
                    $(this).attr('clicktracking', 'off').attr('href', groupUnsubscribeLink);
                    _this.logger?.info(`Group Unsubscribe link replaced`);
                    _this.statsClient?.incr('replaced_group_unsubscribe_link', 1);
                }
            });
        }
        else {
            $(unsubscribeLinkRef).each(function () {
                if (!globalUnsubscribeLink) {
                    _this.logger?.info(`Global Unsubscribe link missing`);
                    _this.statsClient?.incr('global_unsubscribe_link_missing', 1);
                    $(this).attr('href', sendgridUnsubscribeLinkTag);
                }
                else {
                    _this.validateLinkAndLog(globalUnsubscribeLink);
                    $(this).removeAttr('href');
                    $(this).attr('clicktracking', 'off').attr('href', globalUnsubscribeLink);
                    _this.logger?.info(`Global Unsubscribe link replaced`);
                    _this.statsClient?.incr('replaced_global_unsubscribe_link', 1);
                }
            });
        }
        $(preferencesLinkRef).each(function () {
            if (!preferencesLink) {
                $(this.parent?.children).each(function () {
                    if ($(this).text() == ' | ') {
                        $(this).remove();
                    }
                });
                $(this).remove();
                _this.logger?.info(`Preferences link removed from the html body  - ${spaceId}`);
                _this.statsClient?.incr('removed_preferences_link', 1);
            }
            else {
                _this.validateLinkAndLog(preferencesLink);
                $(this).removeAttr('href');
                $(this).attr('clicktracking', 'off').attr('href', preferencesLink);
                _this.logger?.info(`Preferences link replaced  - ${spaceId}`);
                _this.statsClient?.incr('replaced_preferences_link', 1);
            }
        });
        return $.html();
    }
    onResponse(args) {
        const headers = args.response?.headers || args.error?.response?.headers;
        const sgMsgId = headers?.get('X-Message-ID');
        if (sgMsgId)
            args.operation.logs.push('[sendgrid]X-Message-ID: ' + sgMsgId);
    }
}
exports.SendEmailPerformer = SendEmailPerformer;
__decorate([
    (0, actions_shared_1.track)({
        wrapIntegrationError: () => [`Unable to parse templating in email`, `templating parse failure`, 400]
    })
], SendEmailPerformer.prototype, "parseTemplating", null);
__decorate([
    (0, actions_shared_1.track)()
], SendEmailPerformer.prototype, "getBodyTemplateFromS3", null);
__decorate([
    (0, actions_shared_1.track)()
], SendEmailPerformer.prototype, "getBodyHtml", null);
__decorate([
    (0, actions_shared_1.track)({
        wrapIntegrationError: () => ['Unable to export email as HTML', 'Export HTML failure', 400]
    })
], SendEmailPerformer.prototype, "generateEmailHtml", null);
__decorate([
    (0, actions_shared_1.track)()
], SendEmailPerformer.prototype, "performApiLookups", null);
__decorate([
    (0, actions_shared_1.track)()
], SendEmailPerformer.prototype, "validateLinkAndLog", null);
__decorate([
    (0, actions_shared_1.track)()
], SendEmailPerformer.prototype, "insertUnsubscribeLinks", null);
//# sourceMappingURL=SendEmailPerformer.js.map