/** * Processor for "cca-preauth-submit" jobs. * Submits a dental pre-authorization to CCA via the ScionDental portal. */ import { storage } from "../../storage"; import { forwardToSeleniumCCAPreAuthAgent, getSeleniumCCAPreAuthSessionStatus, } from "../../services/seleniumCCAPreAuthClient"; import { io } from "../../socket"; function log(tag: string, msg: string, ctx?: any) { console.log(`${new Date().toISOString()} [${tag}] ${msg}`, ctx ?? ""); } function emitToSocket(socketId: string | undefined, event: string, payload: any) { if (!socketId || !io) return; try { const socket = io.sockets.sockets.get(socketId); if (socket) socket.emit(event, payload); } catch (_) {} } async function pollUntilDone( sessionId: string, pollTimeoutMs = 10 * 60 * 1000 ): Promise { const maxAttempts = 1200; const pollIntervalMs = 500; const maxTransientErrors = 12; let transientErrors = 0; const deadline = Date.now() + pollTimeoutMs; for (let attempt = 0; attempt < maxAttempts; attempt++) { if (Date.now() > deadline) { throw new Error(`CCA preauth polling timeout for session ${sessionId}`); } try { const st = await getSeleniumCCAPreAuthSessionStatus(sessionId); const status: string = st?.status ?? "unknown"; log("cca-preauth-processor", `poll attempt=${attempt}`, { sessionId, status }); transientErrors = 0; if (status === "completed") return st.result; if (status === "error" || status === "not_found") { throw new Error(st?.message || `CCA preauth session ended with status: ${status}`); } await new Promise((r) => setTimeout(r, pollIntervalMs)); } catch (err: any) { const isTerminal = err?.response?.status === 404 || (typeof err?.message === "string" && (err.message.includes("not_found") || err.message.includes("polling timeout"))); if (isTerminal) throw err; transientErrors++; if (transientErrors > maxTransientErrors) { throw new Error(`Too many transient errors polling CCA preauth session ${sessionId}`); } const backoff = Math.min(30_000, 500 * Math.pow(2, transientErrors - 1)); await new Promise((r) => setTimeout(r, backoff)); } } throw new Error(`CCA preauth polling exhausted all attempts for session ${sessionId}`); } export interface CCAPreAuthProcessorInput { enrichedPayload: any; userId: number; claimId?: number; socketId?: string; } export async function runCCAPreAuthProcessor( input: CCAPreAuthProcessorInput, jobId: string ): Promise<{ status: string; authNumber?: string | null; pdfFileId?: number | null }> { const { enrichedPayload, userId, claimId, socketId } = input; log("cca-preauth-processor", "starting Python agent session", { claimId }); const agentResp = await forwardToSeleniumCCAPreAuthAgent(enrichedPayload); if (!agentResp?.session_id) { throw new Error("Python agent did not return a session_id for CCA preauth"); } const sessionId = agentResp.session_id as string; log("cca-preauth-processor", "got session_id", { sessionId }); emitToSocket(socketId, "selenium:cca_preauth_started", { session_id: sessionId, jobId }); const seleniumResult = await pollUntilDone(sessionId); if (!seleniumResult || seleniumResult.status === "error") { throw new Error(seleniumResult?.message ?? "CCA preauth session returned an error"); } const authNumber: string | null = seleniumResult?.authNumber ?? null; const pdfBase64: string = seleniumResult?.pdfBase64 ?? ""; const pdfFilename: string = seleniumResult?.pdfFilename || `cca_preauth_${claimId ?? "unknown"}_${Date.now()}.pdf`; // Save authNumber into the claim record (preauth no column) if (claimId && authNumber) { try { await storage.updateClaim(claimId, { claimNumber: authNumber, status: "APPROVED" } as any); log("cca-preauth-processor", "authNumber saved to claim", { claimId, authNumber }); } catch (e: any) { log("cca-preauth-processor", "failed to save authNumber to claim", { error: e?.message }); } } // Save PDF to patient's PreAuth document group let pdfFileId: number | null = null; if (pdfBase64 && enrichedPayload?.claim?.patientId) { try { const patientId = Number(enrichedPayload.claim.patientId); const pdfBuffer = Buffer.from(pdfBase64, "base64"); let group = await storage.findPdfGroupByPatientTitleKey(patientId, "INSURANCE_CLAIM_PREAUTH"); if (!group) { group = await storage.createPdfGroup(patientId, "PreAuth", "INSURANCE_CLAIM_PREAUTH"); } const created = await storage.createPdfFile(group.id!, pdfFilename, pdfBuffer); if (created && typeof created === "object" && "id" in created) { pdfFileId = Number((created as any).id); } log("cca-preauth-processor", "PDF saved", { pdfFilename, pdfFileId, patientId }); } catch (e: any) { log("cca-preauth-processor", "failed to save PDF", { error: e?.message }); } } emitToSocket(socketId, "selenium:cca_preauth_completed", { jobId, claimId, authNumber, pdfFileId, pdfFilename, message: authNumber ? `CCA pre-authorization submitted — auth number: ${authNumber}` : "CCA pre-authorization submitted successfully", }); log("cca-preauth-processor", "done", { claimId, authNumber }); return { status: "success", authNumber, pdfFileId }; }