import type { Collection } from "mongodb"; import { Client, getClientsCollection, getDb, getFlowsCollection, ObjectId, findSubscriptions, } from "../index"; import { ClientPhoneNumberForFlow, PhoneNumber, PhoneNumberWithFlow, PurchasedPhoneProviderPayload, } from "./phone_numbers.types"; export const getPhoneNumbersCollection = (): Collection => getDb().collection("phone_numbers"); export const getPhoneDataByPhoneNumber = async ( phoneNumber: string, ): Promise => { const phoneCallData = await getPhoneNumbersCollection().findOne({ phone_number: phoneNumber, }); if (!phoneCallData) { throw new Error("PhoneNumber not found"); } const flow = await getFlowsCollection().findOne({ _id: new ObjectId(phoneCallData.flow_id), }); if (!flow) { throw new Error("Flow not found"); } const [subscription] = (await findSubscriptions({ clientId: phoneCallData.client_id, isActive: true, })) || []; return { ...phoneCallData, flow, subscriptionId: subscription?._id?.toString() ?? undefined, }; }; export const getClientPrimaryPhoneNumber = async ( clientId: string, ): Promise => { return ( ( await getPhoneNumbersCollection().findOne({ client_id: clientId, is_primary: true, }) )?.phone_number ?? null ); }; export const getClientPhoneNumber = getClientPrimaryPhoneNumber; export const getClientPhoneData = async ( clientId: string, isPrimary?: boolean, ): Promise => { const filter = { client_id: clientId, is_primary: isPrimary !== false }; const options = isPrimary === false ? { sort: { createdAt: -1 } as const } : {}; return getPhoneNumbersCollection().findOne(filter, options); }; export const getPhoneNumbersForFlows = async ( clientId: string, ): Promise => { const docs = await getPhoneNumbersCollection() .find({ client_id: clientId, flow_id: { $exists: true, $type: "objectId" }, }) .project({ _id: 0, flow_id: 1, phone_number: 1, is_primary: 1 }) .sort({ createdAt: -1 }) .toArray(); return docs.map((doc) => ({ flowId: String(doc.flow_id), phoneNumber: doc.phone_number, isPrimary: doc.is_primary, })); }; export const createPhoneNumberEntity = async ( phoneNumber: string, flowId: string, clientId: string, ): Promise => { const existing = await getClientPhoneData(clientId); const isPrimary = !existing; await getPhoneNumbersCollection().insertOne({ phone_number: phoneNumber, flow_id: new ObjectId(flowId), client_id: clientId, is_primary: isPrimary, createdAt: new Date(), updatedAt: new Date(), }); const phoneNumberData = await getClientPhoneData(clientId, isPrimary); if (!phoneNumberData) throw new Error("Failed to create phoneNumber"); return phoneNumberData; }; /** API-purchased number; `flowId` may be omitted until the number is bound to a flow. */ export const createPurchasedPhoneNumber = async ( phoneNumber: string, providerPayload: PurchasedPhoneProviderPayload, clientId: string, flowId?: string | null, ): Promise => { const existingPhoneForClient = await getClientPhoneData(clientId); const isPrimary = !existingPhoneForClient; const now = new Date(); const trimmedFlowId = flowId?.trim(); const flowObjectId = trimmedFlowId ? new ObjectId(trimmedFlowId) : undefined; const doc: PhoneNumber = { phone_number: phoneNumber, client_id: clientId, is_primary: isPrimary, ...providerPayload, createdAt: now, updatedAt: now, }; if (flowObjectId) { doc.flow_id = flowObjectId; } await getPhoneNumbersCollection().insertOne(doc); const created = await getClientPhoneData(clientId, isPrimary); if (!created) throw new Error("Failed to create phoneNumber"); return created; }; export const findClientByPhoneNumber = async ( phoneNumber: string, ): Promise => { const phoneData = await getPhoneNumbersCollection().findOne({ phone_number: phoneNumber, }); if (!phoneData) throw new Error("Failed to get phone data"); const clientId = phoneData.client_id; const client = await getClientsCollection().findOne({ clientId }); if (!client) throw new Error("Failed to get client"); return client; }; export const getPrimaryPhoneFlowId = async (clientId: string): Promise => { const phone = await getClientPhoneData(clientId); return phone?.flow_id ? String(phone.flow_id) : null; }; export const bindClientPhoneToFlow = async ( clientId: string, flowId: string, ): Promise => { const result = await getPhoneNumbersCollection().updateOne( { client_id: clientId, is_primary: true }, { $set: { flow_id: new ObjectId(flowId), updatedAt: new Date() } }, ); if (result.matchedCount === 0) { throw new Error(`No primary phone number found for client: ${clientId}`); } };