/** * eResidency Application Database Operations */ import { query } from './client'; import { type eResidencyApplication, type eCitizenshipApplication, ApplicationStatus, } from '@the-order/schemas'; /** * Map database row to application object */ function mapRowToApplication(row: any): eResidencyApplication { return { id: row.id, applicantDid: row.applicant_did || undefined, email: row.email, givenName: row.given_name, familyName: row.family_name, dateOfBirth: row.date_of_birth ? (row.date_of_birth instanceof Date ? row.date_of_birth.toISOString().split('T')[0] : row.date_of_birth) : undefined, nationality: row.nationality || undefined, phone: row.phone || undefined, address: row.address ? (typeof row.address === 'string' ? JSON.parse(row.address) : row.address) : undefined, deviceFingerprint: row.device_fingerprint || undefined, identityDocument: row.identity_document ? typeof row.identity_document === 'string' ? JSON.parse(row.identity_document) : row.identity_document : undefined, selfieLiveness: row.selfie_liveness ? typeof row.selfie_liveness === 'string' ? JSON.parse(row.selfie_liveness) : row.selfie_liveness : undefined, status: row.status as ApplicationStatus, submittedAt: row.submitted_at ? (row.submitted_at instanceof Date ? row.submitted_at.toISOString() : row.submitted_at) : undefined, reviewedAt: row.reviewed_at ? (row.reviewed_at instanceof Date ? row.reviewed_at.toISOString() : row.reviewed_at) : undefined, reviewedBy: row.reviewed_by || undefined, rejectionReason: row.rejection_reason || undefined, kycStatus: row.kyc_status || undefined, sanctionsStatus: row.sanctions_status || undefined, pepStatus: row.pep_status || undefined, riskScore: row.risk_score ? parseFloat(String(row.risk_score)) : undefined, kycResults: row.kyc_results ? (typeof row.kyc_results === 'string' ? JSON.parse(row.kyc_results) : row.kyc_results) : undefined, sanctionsResults: row.sanctions_results ? (typeof row.sanctions_results === 'string' ? JSON.parse(row.sanctions_results) : row.sanctions_results) : undefined, riskAssessment: row.risk_assessment ? (typeof row.risk_assessment === 'string' ? JSON.parse(row.risk_assessment) : row.risk_assessment) : undefined, createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : row.created_at, updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : row.updated_at, }; } /** * Create eResidency application */ export async function createEResidencyApplication( application: Omit ): Promise { const result = await query( `INSERT INTO eresidency_applications (applicant_did, email, given_name, family_name, date_of_birth, nationality, phone, address, device_fingerprint, identity_document, selfie_liveness, status, kyc_status, sanctions_status, pep_status) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING *`, [ application.applicantDid || null, application.email, application.givenName, application.familyName, application.dateOfBirth || null, application.nationality || null, application.phone || null, application.address ? JSON.stringify(application.address) : null, application.deviceFingerprint || null, application.identityDocument ? JSON.stringify(application.identityDocument) : null, application.selfieLiveness ? JSON.stringify(application.selfieLiveness) : null, application.status, application.kycStatus || null, application.sanctionsStatus || null, application.pepStatus || null, ] ); return mapRowToApplication(result.rows[0]!); } /** * Get eResidency application by ID */ export async function getEResidencyApplicationById(id: string): Promise { const result = await query( 'SELECT * FROM eresidency_applications WHERE id = $1', [id] ); if (!result.rows[0]) { return null; } return mapRowToApplication(result.rows[0]); } /** * Update eResidency application */ export async function updateEResidencyApplication( id: string, updates: { status?: ApplicationStatus; kycStatus?: 'pending' | 'passed' | 'failed' | 'requires_edd'; sanctionsStatus?: 'pending' | 'clear' | 'flag'; pepStatus?: 'pending' | 'clear' | 'flag'; riskScore?: number; kycResults?: unknown; sanctionsResults?: unknown; riskAssessment?: unknown; reviewedAt?: string; reviewedBy?: string; rejectionReason?: string; } ): Promise { const fields: string[] = []; const values: unknown[] = []; let paramIndex = 1; if (updates.status !== undefined) { fields.push(`status = $${paramIndex++}`); values.push(updates.status); } if (updates.kycStatus !== undefined) { fields.push(`kyc_status = $${paramIndex++}`); values.push(updates.kycStatus); } if (updates.sanctionsStatus !== undefined) { fields.push(`sanctions_status = $${paramIndex++}`); values.push(updates.sanctionsStatus); } if (updates.pepStatus !== undefined) { fields.push(`pep_status = $${paramIndex++}`); values.push(updates.pepStatus); } if (updates.riskScore !== undefined) { fields.push(`risk_score = $${paramIndex++}`); values.push(updates.riskScore); } if (updates.kycResults !== undefined) { fields.push(`kyc_results = $${paramIndex++}`); values.push(JSON.stringify(updates.kycResults)); } if (updates.sanctionsResults !== undefined) { fields.push(`sanctions_results = $${paramIndex++}`); values.push(JSON.stringify(updates.sanctionsResults)); } if (updates.riskAssessment !== undefined) { fields.push(`risk_assessment = $${paramIndex++}`); values.push(JSON.stringify(updates.riskAssessment)); } if (updates.reviewedAt !== undefined) { fields.push(`reviewed_at = $${paramIndex++}`); values.push(updates.reviewedAt); } if (updates.reviewedBy !== undefined) { fields.push(`reviewed_by = $${paramIndex++}`); values.push(updates.reviewedBy); } if (updates.rejectionReason !== undefined) { fields.push(`rejection_reason = $${paramIndex++}`); values.push(updates.rejectionReason); } fields.push(`updated_at = NOW()`); values.push(id); const result = await query( `UPDATE eresidency_applications SET ${fields.join(', ')} WHERE id = $${paramIndex} RETURNING *`, values ); return mapRowToApplication(result.rows[0]!); } /** * Get review queue */ export async function getReviewQueue(filters: { riskBand?: 'low' | 'medium' | 'high'; status?: ApplicationStatus; assignedTo?: string; limit?: number; offset?: number; }): Promise<{ applications: eResidencyApplication[]; total: number }> { const conditions: string[] = []; const params: unknown[] = []; let paramIndex = 1; if (filters.riskBand) { // Map risk band to risk score range const riskRanges: Record<'low' | 'medium' | 'high', [number, number]> = { low: [0, 0.3], medium: [0.3, 0.8], high: [0.8, 1.0], }; const [min, max] = riskRanges[filters.riskBand]; conditions.push(`risk_score >= $${paramIndex++} AND risk_score < $${paramIndex++}`); params.push(min, max); } if (filters.status) { conditions.push(`status = $${paramIndex++}`); params.push(filters.status); } if (filters.assignedTo) { conditions.push(`reviewed_by = $${paramIndex++}`); params.push(filters.assignedTo); } const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; const limit = filters.limit || 50; const offset = filters.offset || 0; // Get total count const countResult = await query<{ count: string }>( `SELECT COUNT(*) as count FROM eresidency_applications ${whereClause}`, params ); const total = parseInt(countResult.rows[0]?.count || '0', 10); // Get applications const result = await query( `SELECT * FROM eresidency_applications ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`, [...params, limit, offset] ); const applications = result.rows.map((row) => mapRowToApplication(row)); return { applications, total }; } /** * Create eCitizenship application */ export async function createECitizenshipApplication( application: Omit ): Promise { const result = await query( `INSERT INTO ecitizenship_applications (applicant_did, resident_did, residency_tenure, sponsor_did, service_merit, video_interview, background_attestations, oath_ceremony, status) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *`, [ application.applicantDid, application.residentDid, application.residencyTenure, application.sponsorDid || null, application.serviceMerit ? JSON.stringify(application.serviceMerit) : null, application.videoInterview ? JSON.stringify(application.videoInterview) : null, application.backgroundAttestations ? JSON.stringify(application.backgroundAttestations) : null, application.oathCeremony ? JSON.stringify(application.oathCeremony) : null, application.status, ] ); const row: any = result.rows[0]!; return { id: row.id, applicantDid: row.applicant_did, residentDid: row.resident_did, residencyTenure: row.residency_tenure || undefined, sponsorDid: row.sponsor_did || undefined, serviceMerit: row.service_merit ? typeof row.service_merit === 'string' ? JSON.parse(row.service_merit) : row.service_merit : undefined, videoInterview: row.video_interview ? typeof row.video_interview === 'string' ? JSON.parse(row.video_interview) : row.video_interview : undefined, backgroundAttestations: row.background_attestations ? typeof row.background_attestations === 'string' ? JSON.parse(row.background_attestations) : row.background_attestations : undefined, oathCeremony: row.oath_ceremony ? typeof row.oath_ceremony === 'string' ? JSON.parse(row.oath_ceremony) : row.oath_ceremony : undefined, status: row.status as ApplicationStatus, submittedAt: row.submitted_at ? (row.submitted_at instanceof Date ? row.submitted_at.toISOString() : row.submitted_at) : undefined, reviewedAt: row.reviewed_at ? (row.reviewed_at instanceof Date ? row.reviewed_at.toISOString() : row.reviewed_at) : undefined, reviewedBy: row.reviewed_by || undefined, rejectionReason: row.rejection_reason || undefined, createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : row.created_at, updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : row.updated_at, }; } /** * Get eCitizenship application by ID */ export async function getECitizenshipApplicationById(id: string): Promise { const result = await query( 'SELECT * FROM ecitizenship_applications WHERE id = $1', [id] ); if (!result.rows[0]) { return null; } const row: any = result.rows[0]!; return { id: row.id, applicantDid: row.applicant_did, residentDid: row.resident_did, residencyTenure: row.residency_tenure || undefined, sponsorDid: row.sponsor_did || undefined, serviceMerit: row.service_merit ? typeof row.service_merit === 'string' ? JSON.parse(row.service_merit) : row.service_merit : undefined, videoInterview: row.video_interview ? typeof row.video_interview === 'string' ? JSON.parse(row.video_interview) : row.video_interview : undefined, backgroundAttestations: row.background_attestations ? typeof row.background_attestations === 'string' ? JSON.parse(row.background_attestations) : row.background_attestations : undefined, oathCeremony: row.oath_ceremony ? typeof row.oath_ceremony === 'string' ? JSON.parse(row.oath_ceremony) : row.oath_ceremony : undefined, status: row.status as ApplicationStatus, submittedAt: row.submitted_at ? (row.submitted_at instanceof Date ? row.submitted_at.toISOString() : row.submitted_at) : undefined, reviewedAt: row.reviewed_at ? (row.reviewed_at instanceof Date ? row.reviewed_at.toISOString() : row.reviewed_at) : undefined, reviewedBy: row.reviewed_by || undefined, rejectionReason: row.rejection_reason || undefined, createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : row.created_at, updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : row.updated_at, }; } /** * Update eCitizenship application */ export async function updateECitizenshipApplication( id: string, updates: { status?: ApplicationStatus; reviewedAt?: string; reviewedBy?: string; rejectionReason?: string; } ): Promise { const fields: string[] = []; const values: unknown[] = []; let paramIndex = 1; if (updates.status !== undefined) { fields.push(`status = $${paramIndex++}`); values.push(updates.status); } if (updates.reviewedAt !== undefined) { fields.push(`reviewed_at = $${paramIndex++}`); values.push(updates.reviewedAt); } if (updates.reviewedBy !== undefined) { fields.push(`reviewed_by = $${paramIndex++}`); values.push(updates.reviewedBy); } if (updates.rejectionReason !== undefined) { fields.push(`rejection_reason = $${paramIndex++}`); values.push(updates.rejectionReason); } fields.push(`updated_at = NOW()`); values.push(id); const result = await query( `UPDATE ecitizenship_applications SET ${fields.join(', ')} WHERE id = $${paramIndex} RETURNING *`, values ); const row: any = result.rows[0]!; return { id: row.id, applicantDid: row.applicant_did, residentDid: row.resident_did, residencyTenure: row.residency_tenure || undefined, sponsorDid: row.sponsor_did || undefined, serviceMerit: row.service_merit ? typeof row.service_merit === 'string' ? JSON.parse(row.service_merit) : row.service_merit : undefined, videoInterview: row.video_interview ? typeof row.video_interview === 'string' ? JSON.parse(row.video_interview) : row.video_interview : undefined, backgroundAttestations: row.background_attestations ? typeof row.background_attestations === 'string' ? JSON.parse(row.background_attestations) : row.background_attestations : undefined, oathCeremony: row.oath_ceremony ? typeof row.oath_ceremony === 'string' ? JSON.parse(row.oath_ceremony) : row.oath_ceremony : undefined, status: row.status as ApplicationStatus, submittedAt: row.submitted_at ? (row.submitted_at instanceof Date ? row.submitted_at.toISOString() : row.submitted_at) : undefined, reviewedAt: row.reviewed_at ? (row.reviewed_at instanceof Date ? row.reviewed_at.toISOString() : row.reviewed_at) : undefined, reviewedBy: row.reviewed_by || undefined, rejectionReason: row.rejection_reason || undefined, createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : row.created_at, updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : row.updated_at, }; }