"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validate = validate;
exports.mergeAndDeduplicateById = mergeAndDeduplicateById;
exports.ensureValidTimestamps = ensureValidTimestamps;
exports.deDuplicateAssociations = deDuplicateAssociations;
const actions_core_1 = require("@segment/actions-core");
const hubspot_list_functions_1 = require("../functions/hubspot-list-functions");
function validate(payloads, flag) {
    const length = payloads.length;
    const cleaned = payloads.filter((payload) => {
        const fieldsToCheck = [
            payload.object_details.id_field_name,
            payload.object_details.id_field_value,
            payload.object_details.object_type
        ];
        return fieldsToCheck.every((field) => field !== null && field !== '');
    });
    if (length === 1 && cleaned.length === 0) {
        throw new actions_core_1.PayloadValidationError('Payload is missing required fields. Null or empty values are not allowed for "Object Type", "ID Field Name" or "ID Field Value".');
    }
    if (flag === true) {
        const hasEngageAudience = cleaned.some((p) => (0, hubspot_list_functions_1.getListPayloadType)(p) === 'is_engage_audience_payload');
        const hasNonEngageAudience = cleaned.some((p) => (0, hubspot_list_functions_1.getListPayloadType)(p) === 'is_non_engage_audience_payload');
        if (hasEngageAudience && hasNonEngageAudience) {
            throw new actions_core_1.PayloadValidationError('Engage and non Engage payloads cannot be mixed in the same batch.');
        }
        const listNames = Array.from(new Set(cleaned.map((p) => (0, hubspot_list_functions_1.getListName)(p)).filter(Boolean)));
        if (listNames.length > 1) {
            throw new actions_core_1.PayloadValidationError(`When updating List membership, all payloads must reference the same list. Found multiple lists in the batch: ${listNames
                .slice(0, 3)
                .join(', ')}`);
        }
    }
    cleaned.forEach((payload) => {
        payload.properties = cleanPropObj(payload.properties);
        payload.sensitive_properties = cleanPropObj(payload.sensitive_properties);
        payload.associations = payload.associations?.filter((association) => {
            const fieldsToCheck = [
                association.id_field_name,
                association.object_type,
                association.id_field_value,
                association.association_label
            ];
            return fieldsToCheck.every((field) => field !== null && field !== '');
        });
        payload.dissociations = payload.dissociations?.filter((association) => {
            const fieldsToCheck = [
                association.id_field_name,
                association.object_type,
                association.id_field_value,
                association.association_label
            ];
            return fieldsToCheck.every((field) => field !== null && field !== '');
        });
    });
    return cleaned;
}
function cleanPropObj(obj) {
    const cleanObj = {};
    if (obj === undefined) {
        return undefined;
    }
    Object.keys(obj).forEach((key) => {
        const value = obj[key];
        const cleanKey = cleanProp(key);
        if (typeof value === 'boolean' || typeof value === 'number') {
            cleanObj[cleanKey] = value;
        }
        else if (typeof value === 'string') {
            if (value.toLowerCase().trim() === 'true' || value.toLowerCase().trim() === 'false') {
                cleanObj[cleanKey] = value.toLowerCase().trim() === 'true';
            }
            else {
                cleanObj[cleanKey] = value.trim();
            }
        }
        else if (typeof value === 'object' && value !== null) {
            cleanObj[cleanKey] = JSON.stringify(value).trim();
        }
        else if (value === null || typeof value === 'undefined') {
            delete cleanObj[cleanKey];
        }
        else {
            cleanObj[cleanKey] = String(value).trim();
        }
    });
    return cleanObj;
}
function cleanProp(str) {
    str = str.toLowerCase().replace(/[^a-z0-9_]/g, '_');
    if (!/^[a-z]/.test(str)) {
        throw new actions_core_1.PayloadValidationError(`Property ${str} in event has an invalid name. Property names must start with a letter.`);
    }
    return str;
}
function mergeAndDeduplicateById(payloads, statsContext) {
    const mergedMap = new Map();
    for (const incoming of payloads) {
        const id = incoming.object_details?.id_field_value;
        if (!id)
            continue;
        const incomingTimestamp = incoming.timestamp ? new Date(incoming.timestamp).getTime() : 0;
        const existing = mergedMap.get(id);
        if (!existing) {
            mergedMap.set(id, { ...incoming });
            continue;
        }
        const existingTimestamp = existing.timestamp ? new Date(existing.timestamp).getTime() : 0;
        statsContext?.statsClient?.incr('hubspot.upsert_object.merged_payload', 1, statsContext?.tags);
        const mergedProps = { ...existing.properties };
        for (const [key, value] of Object.entries(incoming.properties || {})) {
            if (!(key in mergedProps) || incomingTimestamp >= existingTimestamp) {
                mergedProps[key] = value;
            }
        }
        const mergedSensProps = { ...existing.sensitive_properties };
        for (const [key, value] of Object.entries(incoming.sensitive_properties || {})) {
            if (!(key in mergedSensProps) || incomingTimestamp >= existingTimestamp) {
                mergedSensProps[key] = value;
            }
        }
        const existingAssociations = existing.associations || [];
        const incomingAssociations = incoming.associations || [];
        const associationKey = (assoc) => `${assoc.object_type}|${assoc.association_label}|${assoc.id_field_name}|${assoc.id_field_value}`;
        const existingAssocMap = new Map(existingAssociations.map((a) => [associationKey(a), a]));
        for (const assoc of incomingAssociations) {
            const key = associationKey(assoc);
            if (!existingAssocMap.has(key)) {
                statsContext?.statsClient?.incr('hubspot.upsert_object.merged_associations', 1, statsContext?.tags);
                existingAssocMap.set(key, assoc);
            }
        }
        const mergedAssociations = Array.from(existingAssocMap.values());
        mergedMap.set(id, {
            ...existing,
            properties: mergedProps,
            sensitive_properties: mergedSensProps,
            associations: mergedAssociations,
            timestamp: incomingTimestamp >= existingTimestamp ? incoming.timestamp : existing.timestamp
        });
    }
    return Array.from(mergedMap.values()).map(({ timestamp, ...rest }) => rest);
}
function ensureValidTimestamps(payloads, rawData) {
    payloads.forEach((item, index) => {
        if (!isValidTimestamp(item.timestamp)) {
            item.timestamp =
                rawData && Array.isArray(rawData) && rawData[index] && isValidTimestamp(rawData[index]?.timestamp)
                    ? rawData[index]?.timestamp
                    : new Date().toISOString();
        }
    });
    return payloads;
}
function isValidTimestamp(ts) {
    if (typeof ts === 'string' || typeof ts === 'number' || ts instanceof Date) {
        return !isNaN(new Date(ts).getTime());
    }
    return false;
}
function deDuplicateAssociations(associationGroups) {
    if (!associationGroups || associationGroups.length === 0) {
        return associationGroups;
    }
    return associationGroups.map((group) => {
        if (!group || group.length === 0)
            return group;
        const associationKey = (assoc) => JSON.stringify({
            object_type: assoc.object_details.object_type,
            association_label: assoc.association_details.association_label,
            id_field_name: assoc.object_details.id_field_name,
            id_field_value: assoc.object_details.id_field_value
        });
        const uniqueAssociationsMap = new Map();
        for (const assoc of group) {
            uniqueAssociationsMap.set(associationKey(assoc), assoc);
        }
        const dedupedGroup = Array.from(uniqueAssociationsMap.values());
        return dedupedGroup;
    });
}
//# sourceMappingURL=validation-functions.js.map