"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.send = void 0;
exports.mergeValidationResults = mergeValidationResults;
const hashing_utils_1 = require("../../../../lib/hashing-utils");
const actions_core_1 = require("@segment/actions-core");
const schema_functions_1 = require("./schema-functions");
const encryption_functions_1 = require("./encryption-functions");
const event_validator_functions_1 = require("./event-validator-functions");
const constants_1 = require("../../constants");
const send = async (request, settings, payloads) => {
    const anonymousId = payloads[0]?.anonymousId;
    const userId = payloads[0]?.userId;
    const streamId = anonymousId ? anonymousId : userId ? (0, hashing_utils_1.processHashing)(userId, 'sha256', 'hex') : 'unknown';
    const eventSpecMap = await fetchEventSpecsForBatch(request, settings, payloads, streamId);
    const { appVersionPropertyName, publicEncryptionKey, env, apiKey } = settings;
    const isDevOrStaging = env === 'dev' || env === 'staging';
    let encryptionSession;
    if (publicEncryptionKey && isDevOrStaging) {
        try {
            encryptionSession = (0, encryption_functions_1.createEncryptionSession)(publicEncryptionKey);
        }
        catch {
        }
    }
    const json = payloads.map((payload) => {
        const { event, pageUrl, appName, properties, messageId, createdAt } = payload;
        const eventProperties = (0, schema_functions_1.extractSchema)(payload.properties, encryptionSession);
        const eventSpec = eventSpecMap?.get(event) ?? null;
        let eventSpecMetadata;
        if (eventSpec) {
            const validationResult = (0, event_validator_functions_1.validateEvent)(properties, eventSpec);
            mergeValidationResults(eventProperties, validationResult.propertyResults);
            eventSpecMetadata = validationResult.metadata;
        }
        const itemJSON = {
            appName: appName ?? (pageUrl ? pageUrl.split('/')[2] : 'unnamed Segment app'),
            appVersion: appVersionPropertyName && properties[appVersionPropertyName]
                ? properties[appVersionPropertyName]
                : payload.appVersion ?? 'unversioned',
            libVersion: '2.0.0',
            libPlatform: 'Segment',
            messageId,
            createdAt,
            sessionId: '',
            ...(publicEncryptionKey ? { publicEncryptionKey } : {}),
            type: 'event',
            streamId,
            eventName: event,
            eventProperties,
            eventId: null,
            eventHash: null,
            ...(typeof eventSpecMetadata !== 'undefined' ? { eventSpecMetadata } : {})
        };
        return itemJSON;
    });
    if (!json || json.length === 0) {
        throw new actions_core_1.PayloadValidationError('No events generated from payload');
    }
    const endpoint = 'https://api.avo.app/inspector/segment/v1/track';
    return request(endpoint, {
        method: 'post',
        headers: {
            accept: 'application/json',
            'content-type': 'application/json',
            'api-key': apiKey,
            env,
            streamId
        },
        json
    });
};
exports.send = send;
async function fetchEventSpecsForBatch(request, settings, payload, streamId) {
    const { env, apiKey } = settings;
    if (!['dev', 'staging'].includes(env)) {
        return Promise.resolve(null);
    }
    const eventSpecMap = new Map();
    const uniqueEventNames = [...new Set(payload.map((p) => p.event))];
    const requests = uniqueEventNames.map(async (eventName) => {
        const queryParams = new URLSearchParams({
            apiKey,
            streamId,
            eventName
        });
        const url = `${constants_1.DEFAULT_BASE_URL}/trackingPlan/eventSpec?${queryParams.toString()}`;
        try {
            const response = await request(url, {
                method: 'GET',
                headers: {
                    'Content-Type': 'application/json',
                    'User-Agent': 'Segment (Actions)'
                }
            });
            const { events, metadata } = response.data;
            const eventSpec = {
                metadata,
                events: events.map((evt) => {
                    const eventIds = [evt.id, ...evt.vids];
                    return {
                        branchId: evt.b,
                        baseEventId: evt.id,
                        variantIds: evt.vids,
                        props: Object.fromEntries(Object.entries(evt.p).map(([propName, propWire]) => [
                            propName,
                            convertWirePropToConstraints(propWire, eventIds)
                        ]))
                    };
                })
            };
            return { eventName, spec: eventSpec };
        }
        catch {
            return { eventName, spec: null };
        }
    });
    const results = await Promise.all(requests);
    for (const { eventName, spec } of results) {
        eventSpecMap.set(eventName, spec);
    }
    return eventSpecMap;
}
const MAX_WIRE_DEPTH = 2;
function convertWirePropToConstraints(wire, eventIds, depth = 0) {
    const type = wire.t === 'string' ? wire.t : 'object';
    const required = wire.r;
    const isListType = wire.l === true;
    const result = {
        type: isListType ? 'list' : type,
        required,
        isList: isListType
    };
    if (wire.v) {
        if (Array.isArray(wire.v)) {
            const key = JSON.stringify(wire.v);
            result.allowedValues = { [key]: [...eventIds] };
        }
        else if (typeof wire.v === 'object') {
            result.allowedValues = {};
            for (const [allowedArrayJson, vEventIds] of Object.entries(wire.v)) {
                result.allowedValues[allowedArrayJson] = [...vEventIds];
            }
        }
    }
    if (wire.min !== undefined || wire.max !== undefined) {
        const minStr = wire.min !== undefined ? wire.min.toString() : '';
        const maxStr = wire.max !== undefined ? wire.max.toString() : '';
        const key = `${minStr},${maxStr}`;
        result.minMaxRanges = { [key]: [...eventIds] };
    }
    const nestedSchema = depth < MAX_WIRE_DEPTH && wire && typeof wire.t === 'object' && wire.t !== null ? wire.t : undefined;
    if (nestedSchema) {
        result.children = {};
        for (const [childName, childWire] of Object.entries(nestedSchema)) {
            result.children[childName] = convertWirePropToConstraints(childWire, eventIds, depth + 1);
        }
    }
    return result;
}
function isEventProperty(child) {
    return typeof child === 'object' && child !== null && !Array.isArray(child) && 'propertyName' in child;
}
function mergeValidationResults(eventProperties, propertyResults) {
    for (const prop of eventProperties) {
        const validationResult = propertyResults[prop.propertyName];
        if (validationResult) {
            if (validationResult.failedEventIds && validationResult.failedEventIds.length > 0) {
                prop.failedEventIds = validationResult.failedEventIds;
            }
            if (validationResult.passedEventIds && validationResult.passedEventIds.length > 0) {
                prop.passedEventIds = validationResult.passedEventIds;
            }
            if (prop.children && Array.isArray(prop.children) && validationResult.children) {
                const childrenMap = {};
                for (const child of prop.children) {
                    if (isEventProperty(child)) {
                        childrenMap[child.propertyName] = child;
                    }
                }
                for (const [childName, childValidationResult] of Object.entries(validationResult.children)) {
                    const childProp = childrenMap[childName];
                    if (childProp) {
                        if (childValidationResult.failedEventIds && childValidationResult.failedEventIds.length > 0) {
                            childProp.failedEventIds = childValidationResult.failedEventIds;
                        }
                        if (childValidationResult.passedEventIds && childValidationResult.passedEventIds.length > 0) {
                            childProp.passedEventIds = childValidationResult.passedEventIds;
                        }
                        if (childProp.children && Array.isArray(childProp.children) && childValidationResult.children) {
                            const eventPropertyChildren = childProp.children.filter(isEventProperty);
                            mergeValidationResults(eventPropertyChildren, childValidationResult.children);
                        }
                    }
                }
            }
        }
    }
}
//# sourceMappingURL=functions.js.map