// ---------------------------------------------------------------------------
// sender-bind — defensive participant-elementId verification.
//
// dropped per-line senderName, so the closed-set sender-resolution
// step that lived here is gone. What remains is a defensive read pass: every
// supplied participant elementId must exist in the graph, must carry the
// supplied accountId, and must be labelled :Person or :AdminUser. If any
// fails, the bin LOUD-FAILs before the writer attaches PARTICIPANT_IN edges
// to a stale or cross-account node.
//
// The operator confirms participants up front; participants attach to the
// conversation-document :KnowledgeDocument parent via :PARTICIPANT_IN,
// never per-message.
// ---------------------------------------------------------------------------
export class IngestUserFacingError extends Error {
    userFacing = true;
    constructor(message) {
        super(message);
        this.name = "IngestUserFacingError";
    }
}
const CANONICAL_FETCH_CYPHER = `
UNWIND $ids AS id
MATCH (n) WHERE elementId(n) = id
RETURN elementId(n) AS elemId,
       labels(n) AS labels,
       coalesce(n.accountId, '') AS accountId
`;
export async function verifyParticipantElementIds(input) {
    const { session, accountId, ownerElementId, participantElementIds } = input;
    const allIds = [ownerElementId, ...participantElementIds];
    const distinctIds = Array.from(new Set(allIds));
    if (distinctIds.length !== allIds.length) {
        throw new IngestUserFacingError(`participant id list contains duplicates (owner appears in --participant-person-ids?)`);
    }
    const res = await session.executeRead(async (tx) => tx.run(CANONICAL_FETCH_CYPHER, { ids: distinctIds }));
    const seenIds = new Set();
    for (const r of res.records) {
        const elemId = r.get("elemId");
        const labels = r.get("labels") || [];
        const acct = r.get("accountId") || "";
        if (!acct) {
            throw new IngestUserFacingError(`node ${elemId} has no accountId — corrupt canonical Person/AdminUser`);
        }
        if (acct !== accountId) {
            throw new IngestUserFacingError(`node ${elemId} belongs to account ${acct}, not ${accountId}`);
        }
        if (!labels.includes("Person") && !labels.includes("AdminUser")) {
            throw new IngestUserFacingError(`node ${elemId} has labels [${labels.join(",")}]; expected :Person or :AdminUser`);
        }
        seenIds.add(elemId);
    }
    for (const id of distinctIds) {
        if (!seenIds.has(id)) {
            throw new IngestUserFacingError(`elementId ${id} not found in graph`);
        }
    }
    return { participantsResolved: seenIds.size };
}
//# sourceMappingURL=sender-bind.js.map