import { authenticate } from '../../plugins/auth.js' import { getCustomerByFirebaseUid } from '../../services/customer.service.js' import { createPairingRequest, cancelPairingRequest } from '../../services/pairing.service.js' import { getActiveSessionByCustomer, getActiveSessionByCustomerWithUnread, endSession, getCustomerHistory } from '../../services/session.service.js' import { getPricingForCustomer, isValidTier, isCustomerEligibleForFreeTrial, getFreeTrial } from '../../services/pricing.service.js' import { requestExtension } from '../../services/extension.service.js' import { EndedBy } from '../../constants.js' const resolveCustomer = async (request, reply) => { const customer = await getCustomerByFirebaseUid(request.firebaseUser.uid) if (!customer) { return reply.code(404).send({ success: false, error: { code: 'ACCOUNT_NOT_FOUND', message: 'Customer account not found' }, }) } request.customer = customer } export const clientChatRoutes = async (app) => { // Get pricing tiers + free trial eligibility app.get('/pricing', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const pricing = await getPricingForCustomer(request.customer.id) return reply.send({ success: true, data: pricing }) }) app.post('/request', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const { duration_minutes, price, is_free_trial } = request.body || {} // Validate selection if (is_free_trial) { const eligible = await isCustomerEligibleForFreeTrial(request.customer.id) if (!eligible) { return reply.code(403).send({ success: false, error: { code: 'FREE_TRIAL_INELIGIBLE', message: 'Not eligible for free trial' }, }) } const freeTrial = await getFreeTrial() const session = await createPairingRequest(request.customer.id, { duration_minutes: freeTrial.duration_minutes, price: 0, is_free_trial: true, }) return reply.code(201).send({ success: true, data: session }) } if (!duration_minutes || price === undefined) { return reply.code(400).send({ success: false, error: { code: 'BAD_REQUEST', message: 'duration_minutes and price are required' }, }) } if (!(await isValidTier(duration_minutes, price))) { return reply.code(400).send({ success: false, error: { code: 'INVALID_TIER', message: 'Invalid price tier selection' }, }) } const session = await createPairingRequest(request.customer.id, { duration_minutes, price, is_free_trial: false }) return reply.code(201).send({ success: true, data: session }) }) app.post('/request/:sessionId/cancel', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const session = await cancelPairingRequest(request.params.sessionId, request.customer.id) return reply.send({ success: true, data: session }) }) app.get('/session/active', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const session = await getActiveSessionByCustomer(request.customer.id) return reply.send({ success: true, data: session ?? null }) }) app.get('/session/active-with-unread', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const session = await getActiveSessionByCustomerWithUnread(request.customer.id) return reply.send({ success: true, data: session ?? null }) }) app.post('/session/:sessionId/end', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const session = await endSession(request.params.sessionId, EndedBy.CUSTOMER, request.customer.id) return reply.send({ success: true, data: session }) }) // Request session extension app.post('/session/:sessionId/extend', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const { duration_minutes, price } = request.body || {} if (!duration_minutes || price === undefined) { return reply.code(400).send({ success: false, error: { code: 'BAD_REQUEST', message: 'duration_minutes and price are required' }, }) } const extension = await requestExtension(request.params.sessionId, request.customer.id, { duration_minutes, price }) return reply.send({ success: true, data: extension }) }) // Chat history app.get('/history', { preHandler: [authenticate, resolveCustomer] }, async (request, reply) => { const { page, limit } = request.query const history = await getCustomerHistory(request.customer.id, { page: page ? parseInt(page) : 1, limit: limit ? parseInt(limit) : 20, }) return reply.send({ success: true, data: history }) }) }