"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatEndpoint = formatEndpoint;
exports.addToList = addToList;
exports.addToListBatch = addToListBatch;
exports.removeFromList = removeFromList;
exports.removeFromListBatch = removeFromListBatch;
exports.getAccessToken = getAccessToken;
exports.getList = getList;
exports.createList = createList;
const actions_core_1 = require("@segment/actions-core");
const constants_1 = require("./constants");
function formatEndpoint(endpoint) {
    return endpoint.replace('/rest', '');
}
async function addToList(request, settings, payload, statsContext, hookOutputs) {
    const list_id = hookOutputs?.id ?? payload.external_id;
    if (!list_id) {
        const errormessage = 'No list ID found in payload';
        throw new actions_core_1.IntegrationError(errormessage, actions_core_1.ErrorCodes.PAYLOAD_VALIDATION_FAILED, 400);
    }
    const api_endpoint = formatEndpoint(settings.api_endpoint);
    const [csvData] = formatData([payload]);
    const csvSize = Buffer.byteLength(csvData, 'utf8');
    if (csvSize > constants_1.CSV_LIMIT) {
        const errormessage = `CSV data size exceeds limit of ${constants_1.CSV_LIMIT} bytes`;
        statsContext?.statsClient?.incr('addToAudience.error', 1, statsContext?.tags);
        throw new actions_core_1.IntegrationError(errormessage, 'PAYLOAD_TOO_LARGE', 400);
    }
    const url = api_endpoint + constants_1.BULK_IMPORT_ENDPOINT.replace('externalId', list_id).replace('fieldToLookup', payload.lookup_field);
    const response = await request(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'multipart/form-data; boundary=--SEGMENT-DATA--'
        },
        body: createFormData(csvData)
    });
    if (!response.data.success) {
        statsContext?.statsClient?.incr('addToAudience.error', 1, statsContext?.tags);
        parseErrorResponse(response.data);
    }
    statsContext?.statsClient?.incr('addToAudience.success', 1, statsContext?.tags);
    return response.data;
}
async function addToListBatch(request, settings, payloads, statsContext, hookOutputs) {
    const list_id = hookOutputs?.id ?? payloads[0].external_id;
    if (!list_id) {
        return buildMultiStatusErrorResponse(payloads.length, {
            status: 400,
            errortype: actions_core_1.ErrorCodes.PAYLOAD_VALIDATION_FAILED,
            errormessage: 'No list ID found in payload'
        });
    }
    const api_endpoint = formatEndpoint(settings.api_endpoint);
    const [csvData, csvDataItems] = formatData(payloads);
    const csvSize = Buffer.byteLength(csvData, 'utf8');
    if (csvSize > constants_1.CSV_LIMIT) {
        statsContext?.statsClient?.incr('addToAudience.error', payloads.length, statsContext?.tags);
        return buildMultiStatusErrorResponse(payloads.length, {
            status: 400,
            errortype: actions_core_1.ErrorCodes.PAYLOAD_TOO_LARGE,
            errormessage: `CSV data size exceeds limit of ${constants_1.CSV_LIMIT} bytes`
        });
    }
    const url = api_endpoint +
        constants_1.BULK_IMPORT_ENDPOINT.replace('externalId', list_id).replace('fieldToLookup', payloads[0].lookup_field);
    const response = await request(url, {
        method: 'POST',
        throwHttpErrors: false,
        headers: {
            'Content-Type': 'multipart/form-data; boundary=--SEGMENT-DATA--'
        },
        body: createFormData(csvData)
    });
    if (!response.data.success) {
        statsContext?.statsClient?.incr('addToAudience.error', payloads.length, [
            ...(statsContext?.tags ?? []),
            `error_code:${response?.data?.errors?.[0]?.code ?? 'unknown'}`
        ]);
        return parseErrorResponseBatch(response.data, payloads.length);
    }
    statsContext?.statsClient?.incr('addToAudience.success', payloads.length, statsContext?.tags);
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    for (let i = 0; i < payloads.length; i++) {
        multiStatusResponse.setSuccessResponseAtIndex(i, {
            status: 200,
            sent: csvDataItems[i] ?? '',
            body: response.data
        });
    }
    return multiStatusResponse;
}
async function removeFromList(request, settings, payload, statsContext) {
    if (!payload.external_id) {
        throw new actions_core_1.IntegrationError('No "external_id" found in payload', actions_core_1.ErrorCodes.PAYLOAD_VALIDATION_FAILED, 400);
    }
    const api_endpoint = formatEndpoint(settings.api_endpoint);
    const usersToRemove = extractFilterData([payload]);
    const getLeadsUrl = api_endpoint +
        constants_1.GET_LEADS_ENDPOINT.replace('field', payload.lookup_field).replace('emailsToFilter', encodeURIComponent(usersToRemove));
    const getLeadsResponse = await request(getLeadsUrl, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        }
    });
    if (!getLeadsResponse.data.success) {
        statsContext?.statsClient?.incr('removeFromAudience.error', 1, statsContext?.tags);
        parseErrorResponse(getLeadsResponse.data);
    }
    const leadIds = extractLeadIds(getLeadsResponse.data.result);
    const deleteLeadsUrl = api_endpoint + constants_1.REMOVE_USERS_ENDPOINT.replace('listId', payload.external_id).replace('idsToDelete', leadIds);
    const deleteLeadsResponse = await request(deleteLeadsUrl, {
        method: 'DELETE',
        headers: {
            'Content-Type': 'application/json'
        }
    });
    if (!deleteLeadsResponse.data.success) {
        statsContext?.statsClient?.incr('removeFromAudience.error', 1, statsContext?.tags);
        parseErrorResponse(deleteLeadsResponse.data);
    }
    statsContext?.statsClient?.incr('removeFromAudience.success', 1, statsContext?.tags);
    return deleteLeadsResponse.data;
}
async function removeFromListBatch(request, settings, payloads, statsContext) {
    if (!payloads[0].external_id) {
        return buildMultiStatusErrorResponse(payloads.length, {
            status: 400,
            errortype: actions_core_1.ErrorCodes.PAYLOAD_VALIDATION_FAILED,
            errormessage: 'No "external_id" found in payload'
        });
    }
    const api_endpoint = formatEndpoint(settings.api_endpoint);
    const usersToRemove = extractFilterData(payloads);
    const getLeadsUrl = api_endpoint +
        constants_1.GET_LEADS_ENDPOINT.replace('field', payloads[0].lookup_field).replace('emailsToFilter', encodeURIComponent(usersToRemove));
    const getLeadsResponse = await request(getLeadsUrl, {
        method: 'GET',
        throwHttpErrors: false,
        headers: {
            'Content-Type': 'application/json'
        }
    });
    if (!getLeadsResponse.data.success) {
        statsContext?.statsClient?.incr('removeFromAudience.error', payloads.length, [
            ...(statsContext?.tags ?? []),
            `error_code:${getLeadsResponse?.data?.errors?.[0]?.code ?? 'unknown'}`
        ]);
        return parseErrorResponseBatch(getLeadsResponse.data, payloads.length);
    }
    const leadIds = extractLeadIds(getLeadsResponse.data.result);
    const deleteLeadsUrl = api_endpoint + constants_1.REMOVE_USERS_ENDPOINT.replace('listId', payloads[0].external_id).replace('idsToDelete', leadIds);
    const deleteLeadsResponse = await request(deleteLeadsUrl, {
        method: 'DELETE',
        throwHttpErrors: false,
        headers: {
            'Content-Type': 'application/json'
        }
    });
    if (!deleteLeadsResponse.data.success) {
        statsContext?.statsClient?.incr('removeFromAudience.error', payloads.length, [
            ...(statsContext?.tags ?? []),
            `error_code:${deleteLeadsResponse?.data?.errors?.[0]?.code ?? 'unknown'}`
        ]);
        return parseErrorResponseBatch(deleteLeadsResponse.data, payloads.length);
    }
    statsContext?.statsClient?.incr('removeFromAudience.success', payloads.length, statsContext?.tags);
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    for (let i = 0; i < payloads.length; i++) {
        multiStatusResponse.setSuccessResponseAtIndex(i, {
            status: 200,
            sent: getLeadsResponse.data.result[i].id ? `id=${getLeadsResponse.data.result[i].id}` : '',
            body: deleteLeadsResponse.data
        });
    }
    return multiStatusResponse;
}
function createFormData(csvData) {
    const boundary = '--SEGMENT-DATA--';
    const formData = `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="leads.csv"\r\nContent-Type: text/csv\r\n\r\n${csvData}\r\n--${boundary}--\r\n`;
    return formData;
}
function formatData(payloads) {
    if (payloads.length === 0) {
        return ['', []];
    }
    const allKeys = [...new Set(payloads.flatMap((payload) => Object.keys(payload.data)))];
    const header = allKeys.join(',');
    const csvDataItems = payloads.map((payload) => allKeys.map((key) => payload.data[key] || '').join(','));
    const csvData = csvDataItems.join('\n');
    return [`${header}\n${csvData}`, csvDataItems];
}
function extractFilterData(payloads) {
    const data = payloads
        .filter((payload) => payload.field_value !== undefined)
        .map((payload) => payload.field_value)
        .join(',');
    return data;
}
function extractLeadIds(leads = []) {
    const ids = leads.map((lead) => `${lead.id}`).join(',');
    return ids;
}
const MARKETO_NON_RETRYABLE_CODES = new Set([
    '603',
    '605',
    '609',
    '610',
    '612',
    '613',
    '616',
    '701',
    '702',
    '703',
    '704',
    '709',
    '710',
    '711',
    '712',
    '714',
    '718',
    '1001',
    '1002',
    '1003',
    '1004',
    '1005',
    '1006',
    '1007',
    '1008',
    '1009',
    '1010',
    '1011',
    '1012',
    '1013',
    '1014',
    '1015',
    '1017',
    '1018',
    '1021',
    '1022',
    '1025',
    '1026',
    '1027',
    '1028',
    '1035',
    '1036',
    '1037',
    '1042',
    '1048',
    '1049',
    '1076',
    '1077',
    '1078',
    '1079'
]);
function parseErrorResponse(response) {
    if (!response.errors || response.errors.length === 0) {
        throw new actions_core_1.IntegrationError('Unknown error: Marketo returned success=false with no error details', actions_core_1.ErrorCodes.UNKNOWN_ERROR, 500);
    }
    const { code, message } = response.errors[0];
    if (code === '601' || code === '602') {
        throw new actions_core_1.IntegrationError(message, 'INVALID_AUTHENTICATION', 401);
    }
    if (MARKETO_NON_RETRYABLE_CODES.has(code)) {
        throw new actions_core_1.IntegrationError(message, actions_core_1.ErrorCodes.BAD_REQUEST, 400);
    }
    throw new actions_core_1.RetryableError(message);
}
function parseErrorResponseBatch(response, payloadSize) {
    if (!response.errors || response.errors.length === 0) {
        return buildMultiStatusErrorResponse(payloadSize, {
            status: 500,
            errortype: actions_core_1.ErrorCodes.UNKNOWN_ERROR,
            errormessage: 'Unknown error: Marketo returned success=false with no error details'
        });
    }
    const { code, message } = response.errors[0];
    if (code === '601' || code === '602') {
        throw new actions_core_1.IntegrationError(message, actions_core_1.ErrorCodes.INVALID_AUTHENTICATION, 401);
    }
    if (MARKETO_NON_RETRYABLE_CODES.has(code)) {
        return buildMultiStatusErrorResponse(payloadSize, {
            status: 400,
            errortype: actions_core_1.ErrorCodes.BAD_REQUEST,
            body: response.errors[0],
            errormessage: message
        });
    }
    return buildMultiStatusErrorResponse(payloadSize, {
        status: 500,
        errortype: actions_core_1.ErrorCodes.RETRYABLE_ERROR,
        body: response.errors[0],
        errormessage: message
    });
}
async function getAccessToken(request, settings) {
    const api_endpoint = formatEndpoint(settings.api_endpoint);
    const res = await request(`${api_endpoint}/${constants_1.OAUTH_ENDPOINT}`, {
        method: 'POST',
        body: new URLSearchParams({
            client_id: settings.client_id,
            client_secret: settings.client_secret,
            grant_type: 'client_credentials'
        })
    });
    return res.data.access_token;
}
async function getList(request, settings, id) {
    const accessToken = await getAccessToken(request, settings);
    const endpoint = formatEndpoint(settings.api_endpoint);
    const getListUrl = endpoint + constants_1.GET_LIST_ENDPOINT.replace('listId', id);
    const getListResponse = await request(getListUrl, {
        method: 'GET',
        headers: {
            authorization: `Bearer ${accessToken}`
        }
    });
    if (!getListResponse.data.success && getListResponse.data.errors) {
        throw new actions_core_1.IntegrationError(`${getListResponse.data.errors[0].message}`, 'INVALID_RESPONSE', 400);
    }
    if (!getListResponse.data.result) {
        throw new actions_core_1.IntegrationError(`List with ID ${id} not found`, 'INVALID_REQUEST_DATA', 400);
    }
    return {
        successMessage: `Using existing list '${getListResponse.data.result[0].name}' (id: ${id})`,
        savedData: {
            id: id,
            name: getListResponse.data.result[0].name
        }
    };
}
async function createList(request, input, statsContext) {
    const statsClient = statsContext?.statsClient;
    const statsTags = statsContext?.tags;
    if (!input.audienceName) {
        throw new actions_core_1.IntegrationError('Missing audience name value', 'MISSING_REQUIRED_FIELD', 400);
    }
    const endpoint = formatEndpoint(input.settings.api_endpoint);
    const accessToken = await getAccessToken(request, input.settings);
    const getFolderUrl = endpoint + constants_1.GET_FOLDER_ENDPOINT.replace('folderName', encodeURIComponent(input.settings.folder_name));
    const getFolderResponse = await request(getFolderUrl, {
        method: 'GET',
        headers: {
            authorization: `Bearer ${accessToken}`
        }
    });
    if (!getFolderResponse.data.success && getFolderResponse.data.errors) {
        statsClient?.incr('createAudience.error', 1, statsTags);
        throw new actions_core_1.IntegrationError(`${getFolderResponse.data.errors[0].message}`, 'INVALID_RESPONSE', 400);
    }
    if (!getFolderResponse.data.result) {
        statsClient?.incr('createAudience.error', 1, statsTags);
        throw new actions_core_1.IntegrationError(`A folder with the name ${input.settings.folder_name} not found`, 'INVALID_REQUEST_DATA', 400);
    }
    const folderId = getFolderResponse.data.result[0].id.toString();
    const createListUrl = endpoint +
        constants_1.CREATE_LIST_ENDPOINT.replace('folderId', folderId).replace('listName', encodeURIComponent(input.audienceName));
    const createListResponse = await request(createListUrl, {
        method: 'POST',
        headers: {
            authorization: `Bearer ${accessToken}`
        }
    });
    if (!createListResponse.data.success && createListResponse.data.errors) {
        statsClient?.incr('createAudience.error', 1, statsTags);
        throw new actions_core_1.IntegrationError(`${createListResponse.data.errors[0].message}`, 'INVALID_RESPONSE', 400);
    }
    const listId = createListResponse.data.result[0].id.toString();
    statsClient?.incr('createAudience.success', 1, statsTags);
    return listId;
}
const buildMultiStatusErrorResponse = (payloadSize, error) => {
    const multiStatusResponse = new actions_core_1.MultiStatusResponse();
    for (let i = 0; i < payloadSize; i++) {
        multiStatusResponse.setErrorResponseAtIndex(i, error);
    }
    return multiStatusResponse;
};
//# sourceMappingURL=functions.js.map