"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.send = send;
exports.validate = validate;
exports.validatePropType = validatePropType;
exports.getEventid = getEventid;
exports.cleanKey = cleanKey;
exports.searchDefinedEvents = searchDefinedEvents;
exports.validatePropertyDefinitions = validatePropertyDefinitions;
exports.defineNewEvent = defineNewEvent;
const actions_core_1 = require("@segment/actions-core");
const constants_1 = require("./constants");
const client_1 = require("./client");
const snakeCase_1 = __importDefault(require("lodash/snakeCase"));
async function send(request, settings, payload) {
    const validPayload = validate(payload);
    const { unixTimestamp13, optEventProperties, uuid, endUserId: visitor_id, anonymizeIP: anonymize_ip, eventSyncConfig: { eventId, eventKey, shouldSnakeCaseEventKey }, tags, standardEventProperties: { revenue, value, quantity } = {}, sessionId: session_id } = validPayload;
    const client = new client_1.OptimizelyWebClient(request, settings);
    const entity_id = eventId ?? (await getEventid(client, validPayload));
    const body = {
        account_id: settings.optimizelyAccountId,
        project_id: settings.projectID,
        anonymize_ip,
        client_name: 'twilio_segment/optimizely_web_destination',
        client_version: '1.0.0',
        enrich_decisions: true,
        visitors: [
            {
                visitor_id,
                session_id,
                attributes: [],
                snapshots: [
                    {
                        decisions: [],
                        events: [
                            {
                                entity_id,
                                key: cleanKey(shouldSnakeCaseEventKey, eventKey),
                                quantity,
                                revenue: typeof revenue === 'number' ? revenue * 100 : undefined,
                                tags: {
                                    ...tags,
                                    ...(Object.keys(optEventProperties ?? {}).length > 0 && {
                                        $opt_event_properties: optEventProperties
                                    })
                                },
                                timestamp: unixTimestamp13,
                                type: 'other',
                                uuid,
                                value
                            }
                        ]
                    }
                ]
            }
        ]
    };
    return await client.sendEvent(body);
}
function validate(payload) {
    const { eventSyncConfig: { eventKey, eventId, shouldSnakeCaseEventKey }, tags, timestamp, customStringProperties, customNumericProperties, customBooleanProperties } = payload;
    if (!eventKey && !eventId) {
        throw new actions_core_1.PayloadValidationError('One of "Event Key" or "Event Id" is required');
    }
    const unixTimestamp13 = new Date(timestamp).getTime();
    if (unixTimestamp13.toString().length !== 13) {
        throw new actions_core_1.PayloadValidationError('Unable to convert timestamp into 13 digit Unix timestamp');
    }
    if (!validatePropType(customStringProperties, ['string'])) {
        throw new actions_core_1.PayloadValidationError('Custom String Properties must be of type string');
    }
    if (!validatePropType(customNumericProperties, ['number'])) {
        throw new actions_core_1.PayloadValidationError('Custom Numeric Properties must be of type number');
    }
    if (!validatePropType(customBooleanProperties, ['boolean'])) {
        throw new actions_core_1.PayloadValidationError('Custom Boolean Properties must be of type boolean');
    }
    if (!validatePropType(tags, ['string', 'number'])) {
        throw new actions_core_1.PayloadValidationError('Tags must be of type string or number');
    }
    const key = cleanKey(shouldSnakeCaseEventKey, eventKey);
    const optEventProperties = getCustomProps(payload);
    return {
        ...payload,
        unixTimestamp13,
        optEventProperties,
        key
    };
}
function getCustomProps(payload) {
    const { customStringProperties, customNumericProperties, customBooleanProperties } = payload;
    return {
        ...customStringProperties,
        ...customNumericProperties,
        ...customBooleanProperties
    };
}
function validatePropType(obj, typesToCheck = ['string', 'boolean', 'number']) {
    if (obj === undefined || Object.keys(obj).length === 0) {
        return true;
    }
    return Object.values(obj).every((value) => {
        const type = typeof value;
        return typesToCheck.includes(type);
    });
}
async function getEventid(client, payload) {
    const { eventSyncConfig: { createEventIfNotFound, eventId }, key } = payload;
    let event_id = eventId ?? (await searchDefinedEvents(client, key));
    if (event_id === undefined && createEventIfNotFound !== 'CREATE') {
        throw new actions_core_1.PayloadValidationError(`Event with key = ${key} not found in Optimizely. The "Create If Not Found" field is set to "Do not create", which prevents Segment from defining the event in Optimizely.`);
    }
    if (event_id === undefined) {
        const newEventItemWithProps = await defineNewEvent(client, key, payload);
        event_id = newEventItemWithProps.id.toString();
        compareProps(payload.optEventProperties, newEventItemWithProps.event_properties, event_id);
    }
    else {
        await validatePropertyDefinitions(client, event_id, payload);
    }
    if (!event_id) {
        throw new actions_core_1.PayloadValidationError(`Error attempting to find event with key = ${key} in Optimizely`);
    }
    return event_id;
}
function cleanKey(shouldSnakeCaseEventKey, key) {
    if (!key) {
        return undefined;
    }
    return shouldSnakeCaseEventKey ? (0, snakeCase_1.default)(key) : key;
}
async function searchDefinedEvents(client, key) {
    const response = await client.getCustomEvents();
    const eventItems = await response.json();
    const event = eventItems.find((event) => {
        return event.key === key;
    });
    return event?.id.toString() ?? undefined;
}
async function validatePropertyDefinitions(client, event_id, payload) {
    const { optEventProperties } = payload;
    const response = await client.getCustomEvent(event_id);
    const eventItem = await response.json();
    const definedProps = eventItem.event_properties ?? [];
    compareProps(optEventProperties, definedProps, event_id);
}
function compareProps(optEventProperties = {}, definedProps = [], event_id) {
    Object.entries(optEventProperties ?? {}).forEach(([k, v]) => {
        const match = definedProps.find((p) => p.name == k);
        if (!match) {
            throw new actions_core_1.PayloadValidationError(`Property: '${k}' is not defined in Optimizely event with event_id: '${event_id}'. Please define the property in Optimizely or remove it from the event.`);
        }
        if (match.data_type !== typeof v) {
            throw new actions_core_1.PayloadValidationError(`Property: '${k}' is of type ${typeof v} but defined in Optimizely event with event_id: '${event_id}' as ${match.data_type}`);
        }
    });
}
async function defineNewEvent(client, key, payload) {
    const { optEventProperties = {} } = payload;
    if (Object.keys(optEventProperties).length > constants_1.MAX_CUSTOM_PROPS_PER_EVENT) {
        throw new actions_core_1.PayloadValidationError(`Optimizely supports a maximum of ${constants_1.MAX_CUSTOM_PROPS_PER_EVENT} custom properties per event.`);
    }
    const response = await client.createCustomEvent(key, optEventProperties);
    const event = await response.json();
    return event;
}
//# sourceMappingURL=utils.js.map