"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendTrackEvent = sendTrackEvent;
exports.sendBatchedTrackEvent = sendBatchedTrackEvent;
exports.sendTrackPurchase = sendTrackPurchase;
exports.sendBatchedTrackPurchase = sendBatchedTrackPurchase;
exports.updateUserProfile = updateUserProfile;
exports.updateBatchedUserProfile = updateBatchedUserProfile;
exports.generateMultiStatusError = generateMultiStatusError;
const actions_core_1 = require("@segment/actions-core");
const actions_core_2 = require("@segment/actions-core");
const dayjs_1 = __importDefault(require("dayjs"));
const trackPurchase_1 = __importDefault(require("./trackPurchase"));
const userAlias_1 = require("./userAlias");
const actions_core_3 = require("@segment/actions-core");
const constants_1 = require("./constants");
function toISO8601(date) {
    if (date === null || date === undefined) {
        return date;
    }
    const d = (0, dayjs_1.default)(date);
    return d.isValid() ? d.toISOString() : undefined;
}
function toDateFormat(date, format) {
    if (date === null || date === undefined) {
        return date;
    }
    const d = (0, dayjs_1.default)(date);
    return d.isValid() ? d.format(format) : undefined;
}
function removeEmpty(obj) {
    if (!obj) {
        return obj;
    }
    const cleaned = (0, actions_core_2.removeUndefined)(obj);
    if (typeof cleaned === 'object' && Object.keys(cleaned).length > 0) {
        return cleaned;
    }
    return undefined;
}
function toBrazeGender(gender) {
    if (!gender) {
        return gender;
    }
    const genders = {
        M: ['man', 'male', 'm'],
        F: ['woman', 'female', 'w', 'f'],
        O: ['other', 'o'],
        N: ['not applicable', 'n'],
        P: ['prefer not to say', 'p']
    };
    const brazeGender = Object.keys(genders).find((key) => genders[key].includes(gender.toLowerCase()));
    return brazeGender || gender;
}
function sendTrackEvent(request, settings, payload, syncMode) {
    const { braze_id, external_id, email } = payload;
    const user_alias = (0, userAlias_1.getUserAlias)(payload.user_alias);
    if (!braze_id && !user_alias && !external_id && !email) {
        throw new actions_core_2.IntegrationError('One of "external_id" or "user_alias" or "braze_id" or "email" is required.', 'Missing required fields', 400);
    }
    let updateExistingOnly = payload._update_existing_only;
    if (syncMode) {
        updateExistingOnly = syncMode === 'update';
    }
    return request(`${settings.endpoint}/users/track`, {
        method: 'post',
        json: {
            events: [
                {
                    braze_id,
                    external_id,
                    email,
                    user_alias,
                    app_id: settings.app_id,
                    name: payload.name,
                    time: toISO8601(payload.time),
                    properties: payload.properties,
                    _update_existing_only: updateExistingOnly
                }
            ]
        }
    });
}
async function sendBatchedTrackEvent(request, settings, payloads, syncMode) {
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    const filteredPayloads = [];
    const validPayloadIndicesBitmap = [];
    payloads.forEach((payload, originalBatchIndex) => {
        const { braze_id, external_id, email } = payload;
        const user_alias = (0, userAlias_1.getUserAlias)(payload.user_alias);
        if (!braze_id && !user_alias && !external_id && !email) {
            multiStatusResponse.setErrorResponseAtIndex(originalBatchIndex, {
                status: 400,
                errortype: 'PAYLOAD_VALIDATION_FAILED',
                errormessage: 'One of "external_id" or "user_alias" or "braze_id" or "email" is required.'
            });
            return;
        }
        let updateExistingOnly = payload._update_existing_only;
        if (syncMode) {
            updateExistingOnly = syncMode === 'update';
        }
        const payloadToSend = {
            braze_id,
            external_id,
            email,
            user_alias,
            app_id: settings.app_id,
            name: payload.name,
            time: toISO8601(payload.time),
            properties: payload.properties,
            _update_existing_only: updateExistingOnly
        };
        filteredPayloads.push(payloadToSend);
        validPayloadIndicesBitmap.push(originalBatchIndex);
        payloads[originalBatchIndex] = payloadToSend;
        multiStatusResponse.setSuccessResponseAtIndex(originalBatchIndex, {
            status: 200,
            sent: payloadToSend,
            body: 'success'
        });
    });
    const response = request(`${settings.endpoint}/users/track`, {
        method: 'post',
        ...(filteredPayloads.length > 1 ? { headers: { 'X-Braze-Batch': 'true' } } : undefined),
        json: {
            events: filteredPayloads
        }
    });
    await handleBrazeAPIResponse(payloads, response, multiStatusResponse, validPayloadIndicesBitmap);
    return multiStatusResponse;
}
function sendTrackPurchase(request, settings, payload, syncMode) {
    const { braze_id, external_id, email } = payload;
    const user_alias = (0, userAlias_1.getUserAlias)(payload.user_alias);
    if (!braze_id && !user_alias && !external_id && !email) {
        throw new actions_core_2.IntegrationError('One of "external_id" or "user_alias" or "braze_id" or "email" is required.', 'Missing required fields', 400);
    }
    if (payload.products.length === 0) {
        return;
    }
    const reservedKeys = Object.keys(trackPurchase_1.default.fields.products.properties ?? {});
    const event_properties = (0, actions_core_1.omit)(payload.properties, ['products']);
    let updateExistingOnly = payload._update_existing_only;
    if (syncMode) {
        updateExistingOnly = syncMode === 'update';
    }
    const base = {
        braze_id,
        external_id,
        email,
        user_alias,
        app_id: settings.app_id,
        time: toISO8601(payload.time),
        _update_existing_only: updateExistingOnly
    };
    return request(`${settings.endpoint}/users/track`, {
        method: 'post',
        ...(payload.products.length > 1 ? { headers: { 'X-Braze-Batch': 'true' } } : undefined),
        json: {
            purchases: payload.products.map(function (product) {
                return {
                    ...base,
                    product_id: product.product_id,
                    currency: product.currency ?? 'USD',
                    price: product.price,
                    quantity: product.quantity,
                    properties: {
                        ...(0, actions_core_1.omit)(product, reservedKeys),
                        ...event_properties
                    }
                };
            })
        }
    });
}
async function sendBatchedTrackPurchase(request, settings, payloads, syncMode) {
    let objectCount = 0;
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    const flattenedPayload = [];
    const validPayloadIndicesBitmap = [];
    const reservedKeys = Object.keys(trackPurchase_1.default.fields.products.properties ?? {});
    payloads.forEach((payload, originalBatchIndex) => {
        const { braze_id, external_id, email, products } = payload;
        if (Array.isArray(products) && products.length > constants_1.MAX_BATCH_SIZE) {
            multiStatusResponse.setErrorResponseAtIndex(originalBatchIndex, {
                status: 413,
                errortype: 'PAYLOAD_TOO_LARGE',
                body: payload,
                errormessage: `Max batch size exceeded. This payload contains ${products.length} products, which is more than Braze's maximum batch size of ${constants_1.MAX_BATCH_SIZE} objects per batch.`
            });
            return;
        }
        objectCount += Array.isArray(products) ? products.length : 0;
        if (objectCount > constants_1.MAX_BATCH_SIZE) {
            multiStatusResponse.setErrorResponseAtIndex(originalBatchIndex, {
                status: 429,
                errortype: 'RETRYABLE_ERROR',
                body: payload,
                errormessage: `Max batch size exceeded. This event was included in a batch which then exceeded Braze's maximum batch size of ${constants_1.MAX_BATCH_SIZE} objects per batch. This payload will be retried in another batch.`
            });
            return;
        }
        const user_alias = (0, userAlias_1.getUserAlias)(payload.user_alias);
        if (!braze_id && !user_alias && !external_id && !email) {
            multiStatusResponse.setErrorResponseAtIndex(originalBatchIndex, {
                status: 400,
                errortype: 'PAYLOAD_VALIDATION_FAILED',
                errormessage: 'One of "external_id" or "user_alias" or "braze_id" or "email" is required.'
            });
            return;
        }
        if (products.length === 0) {
            multiStatusResponse.setErrorResponseAtIndex(originalBatchIndex, {
                status: 400,
                errortype: 'PAYLOAD_VALIDATION_FAILED',
                body: payload,
                errormessage: 'This event was not sent to Braze because it did not contain any products.'
            });
            return;
        }
        let updateExistingOnly = payload._update_existing_only;
        if (syncMode) {
            updateExistingOnly = syncMode === 'update';
        }
        const requestBase = {
            braze_id,
            external_id,
            user_alias,
            email,
            app_id: settings.app_id,
            time: toISO8601(payload.time),
            _update_existing_only: updateExistingOnly
        };
        const eventProperties = (0, actions_core_1.omit)(payload.properties, ['products']);
        const flattenedPayloadGroup = [];
        products.forEach((product) => {
            const flattenedPayloadItem = {
                ...requestBase,
                product_id: product.product_id,
                currency: product.currency ?? 'USD',
                price: product.price,
                quantity: product.quantity,
                properties: {
                    ...(0, actions_core_1.omit)(product, reservedKeys),
                    ...eventProperties
                }
            };
            flattenedPayload.push(flattenedPayloadItem);
            flattenedPayloadGroup.push(flattenedPayloadItem);
            validPayloadIndicesBitmap.push(originalBatchIndex);
            payloads[originalBatchIndex] = flattenedPayloadGroup;
        });
        multiStatusResponse.setSuccessResponseAtIndex(originalBatchIndex, {
            status: 200,
            sent: payload,
            body: 'success'
        });
    });
    if (flattenedPayload.length === 0) {
        return multiStatusResponse;
    }
    const response = request(`${settings.endpoint}/users/track`, {
        method: 'post',
        ...(flattenedPayload.length > 1 ? { headers: { 'X-Braze-Batch': 'true' } } : undefined),
        json: {
            purchases: flattenedPayload
        }
    });
    await handleBrazeAPIResponse(payloads, response, multiStatusResponse, validPayloadIndicesBitmap);
    return multiStatusResponse;
}
function updateUserProfile(request, settings, payload, syncMode) {
    const { braze_id, external_id, email } = payload;
    const user_alias = (0, userAlias_1.getUserAlias)(payload.user_alias);
    if (!braze_id && !user_alias && !external_id && !email) {
        throw new actions_core_2.IntegrationError('One of "external_id" or "user_alias" or "braze_id" or "email" is required.', 'Missing required fields', 400);
    }
    const reservedKeys = Object.keys(trackPurchase_1.default.fields);
    reservedKeys.push('firstName', 'lastName', 'avatar');
    const customAttrs = (0, actions_core_1.omit)(payload.custom_attributes, reservedKeys);
    let updateExistingOnly = payload._update_existing_only;
    if (syncMode) {
        updateExistingOnly = syncMode === 'update';
    }
    return request(`${settings.endpoint}/users/track`, {
        method: 'post',
        json: {
            attributes: [
                {
                    ...customAttrs,
                    braze_id,
                    external_id,
                    user_alias,
                    country: payload.country,
                    current_location: removeEmpty(payload.current_location),
                    date_of_first_session: toISO8601(payload.date_of_first_session),
                    date_of_last_session: toISO8601(payload.date_of_last_session),
                    dob: toDateFormat(payload.dob, 'YYYY-MM-DD'),
                    email: payload.email,
                    email_subscribe: payload.email_subscribe,
                    email_open_tracking_disabled: payload.email_open_tracking_disabled,
                    email_click_tracking_disabled: payload.email_click_tracking_disabled,
                    facebook: payload.facebook,
                    first_name: payload.first_name,
                    gender: toBrazeGender(payload.gender),
                    home_city: payload.home_city,
                    image_url: payload.image_url,
                    language: payload.language,
                    last_name: payload.last_name,
                    marked_email_as_spam_at: toISO8601(payload.marked_email_as_spam_at),
                    phone: payload.phone,
                    push_subscribe: payload.push_subscribe,
                    push_tokens: payload.push_tokens,
                    time_zone: payload.time_zone,
                    twitter: payload.twitter,
                    ...(typeof payload.subscription_groups === 'object' &&
                        Array.isArray(payload.subscription_groups) &&
                        payload.subscription_groups.length > 0
                        ? { subscription_groups: payload.subscription_groups }
                        : {}),
                    _update_existing_only: updateExistingOnly
                }
            ]
        }
    });
}
async function updateBatchedUserProfile(request, settings, payloads, syncMode) {
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    const filteredPayloads = [];
    const validPayloadIndicesBitmap = [];
    const reservedKeys = Object.keys(trackPurchase_1.default.fields);
    reservedKeys.push('firstName', 'lastName', 'avatar');
    payloads.forEach((payload, originalBatchIndex) => {
        const { braze_id, external_id, email } = payload;
        const user_alias = (0, userAlias_1.getUserAlias)(payload.user_alias);
        if (!braze_id && !user_alias && !external_id && !email) {
            multiStatusResponse.setErrorResponseAtIndex(originalBatchIndex, {
                status: 400,
                errortype: 'PAYLOAD_VALIDATION_FAILED',
                errormessage: 'One of "external_id" or "user_alias" or "braze_id" or "email" is required.'
            });
            return;
        }
        const customAttrs = (0, actions_core_1.omit)(payload.custom_attributes, reservedKeys);
        let updateExistingOnly = payload._update_existing_only;
        if (syncMode) {
            updateExistingOnly = syncMode === 'update';
        }
        const payloadToSend = {
            ...customAttrs,
            braze_id,
            external_id,
            user_alias,
            country: payload.country,
            current_location: removeEmpty(payload.current_location),
            date_of_first_session: toISO8601(payload.date_of_first_session),
            date_of_last_session: toISO8601(payload.date_of_last_session),
            dob: toDateFormat(payload.dob, 'YYYY-MM-DD'),
            email,
            email_subscribe: payload.email_subscribe,
            email_open_tracking_disabled: payload.email_open_tracking_disabled,
            email_click_tracking_disabled: payload.email_click_tracking_disabled,
            facebook: payload.facebook,
            first_name: payload.first_name,
            gender: toBrazeGender(payload.gender),
            home_city: payload.home_city,
            image_url: payload.image_url,
            language: payload.language,
            last_name: payload.last_name,
            marked_email_as_spam_at: toISO8601(payload.marked_email_as_spam_at),
            phone: payload.phone,
            push_subscribe: payload.push_subscribe,
            push_tokens: payload.push_tokens,
            time_zone: payload.time_zone,
            twitter: payload.twitter,
            ...(typeof payload.subscription_groups === 'object' &&
                Array.isArray(payload.subscription_groups) &&
                payload.subscription_groups.length > 0
                ? { subscription_groups: payload.subscription_groups }
                : {}),
            _update_existing_only: updateExistingOnly
        };
        filteredPayloads.push(payloadToSend);
        validPayloadIndicesBitmap.push(originalBatchIndex);
        payloads[originalBatchIndex] = payloadToSend;
        multiStatusResponse.setSuccessResponseAtIndex(originalBatchIndex, {
            status: 200,
            sent: payloadToSend,
            body: 'success'
        });
    });
    const response = request(`${settings.endpoint}/users/track`, {
        method: 'post',
        ...(filteredPayloads.length > 1 ? { headers: { 'X-Braze-Batch': 'true' } } : undefined),
        json: {
            attributes: filteredPayloads
        }
    });
    await handleBrazeAPIResponse(payloads, response, multiStatusResponse, validPayloadIndicesBitmap);
    return multiStatusResponse;
}
async function handleBrazeAPIResponse(payloads, apiResponse, multiStatusResponse, validPayloadIndicesBitmap) {
    try {
        const response = await apiResponse;
        if (response.data.errors && Array.isArray(response.data.errors)) {
            response.data.errors.forEach((error) => {
                const indexInOriginalPayload = validPayloadIndicesBitmap[error.index];
                if (multiStatusResponse.isErrorResponseAtIndex(indexInOriginalPayload)) {
                    return;
                }
                multiStatusResponse.setErrorResponseAtIndex(indexInOriginalPayload, {
                    status: 400,
                    errortype: 'BAD_REQUEST',
                    errormessage: error.type,
                    sent: payloads[indexInOriginalPayload],
                    body: error.type
                });
            });
        }
    }
    catch (error) {
        if (error instanceof actions_core_3.HTTPError) {
            const errorResponse = error.response;
            const parsedErrors = new Array(payloads.length).fill(new Set());
            if (errorResponse.data.errors && Array.isArray(errorResponse.data.errors)) {
                errorResponse.data.errors.forEach((error) => {
                    const indexInOriginalPayload = validPayloadIndicesBitmap[error.index];
                    parsedErrors[indexInOriginalPayload].add(error.type);
                });
            }
            for (let i = 0; i < multiStatusResponse.length(); i++) {
                if (multiStatusResponse.isErrorResponseAtIndex(i)) {
                    continue;
                }
                multiStatusResponse.setErrorResponseAtIndex(i, {
                    status: error.response.status,
                    errormessage: error?.response?.data?.message ?? error.message,
                    sent: payloads[i],
                    body: parsedErrors[i].size > 0 ? Array.from(parsedErrors[i]).join(', ') : undefined
                });
            }
        }
        else {
            throw error;
        }
    }
}
function generateMultiStatusError(batchSize, errorMessage) {
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    for (let i = 0; i < batchSize; i++) {
        multiStatusResponse.pushErrorResponse({
            status: 400,
            errortype: 'PAYLOAD_VALIDATION_FAILED',
            errormessage: errorMessage
        });
    }
    return multiStatusResponse;
}
//# sourceMappingURL=utils.js.map