import { Router, Request, Response } from "express"; import { storage } from "../storage"; import { enqueueSeleniumJob } from "../queue/jobRunner"; import { forwardOtpToSeleniumTuftsSCOPreAuthAgent } from "../services/seleniumTuftsSCOPreAuthClient"; import { io } from "../socket"; const router = Router(); /** * POST /tuftssco-preauth * * Enqueues a Tufts SCO (DentaQuest) pre-authorization submission job. * Uses persistent session + OTP handling, same pattern as UnitedDH preauth. * * Body fields (JSON): * data — preauth payload (memberId, dateOfBirth, serviceDate, serviceLines, patientName, etc.) * socketId — socket.io client id * claimId — existing claim DB id (optional) */ router.post("/tuftssco-preauth", async (req: Request, res: Response): Promise => { if (!req.user?.id) { return res.status(401).json({ error: "Unauthorized: user info missing" }); } try { const claimData = typeof req.body.data === "string" ? JSON.parse(req.body.data) : req.body.data ?? req.body ?? {}; const credentials = await storage.getInsuranceCredentialByUserAndSiteKey( req.user.id, "TUFTS_SCO" ); if (!credentials) { return res.status(404).json({ error: "No Tufts SCO credentials found. Please add them on the Settings page.", }); } const enrichedPayload = { claim: { ...claimData, dentaquestUsername: credentials.username, dentaquestPassword: credentials.password, }, }; const socketId: string | undefined = req.body.socketId; let claimId: number | undefined = claimData.claimId ? Number(claimData.claimId) : undefined; if (!claimId && claimData.patientId) { try { const serviceDate = claimData.serviceDate ? new Date(claimData.serviceDate) : new Date(); const dob = claimData.dateOfBirth ? new Date(claimData.dateOfBirth) : new Date("2000-01-01"); const record = await storage.createClaim({ patientId: Number(claimData.patientId), appointmentId: claimData.appointmentId ? Number(claimData.appointmentId) : null, userId: req.user.id, staffId: Number(claimData.staffId) || 1, patientName: claimData.patientName || "", memberId: claimData.memberId || "", dateOfBirth: dob, remarks: claimData.remarks || "", missingTeethStatus: claimData.missingTeethStatus || "No_missing", serviceDate, insuranceProvider: "Tufts SCO", status: "PREAUTH", } as any); claimId = record.id; console.log(`[tuftssco-preauth route] created claim record id=${claimId}`); } catch (e: any) { console.error("[tuftssco-preauth route] failed to create claim record:", e?.message); } } const jobId = enqueueSeleniumJob({ jobType: "tuftssco-preauth-submit", userId: req.user.id, socketId, enrichedPayload, claimId, }); return res.json({ status: "queued", jobId }); } catch (err: any) { console.error("[tuftssco-preauth route] error:", err); return res.status(500).json({ error: err.message || "Failed to enqueue Tufts SCO preauth job", }); } }); /** * POST /claims/tuftssco-preauth/selenium/submit-otp * Body: { session_id, otp, socketId? } */ router.post("/tuftssco-preauth/selenium/submit-otp", async (req: Request, res: Response): Promise => { const { session_id: sessionId, otp, socketId } = req.body; if (!sessionId || !otp) { return res.status(400).json({ error: "session_id and otp are required" }); } try { const r = await forwardOtpToSeleniumTuftsSCOPreAuthAgent(sessionId, otp); if (socketId && io) { io.to(socketId).emit("selenium:otp_submitted", { session_id: sessionId }); } return res.json(r); } catch (err: any) { console.error("[tuftssco-preauth] submit-otp failed:", err?.message); return res.status(500).json({ error: err?.message || "Failed to forward OTP" }); } }); export default router;