import type { Pool } from 'pg'; import type { CreateDeepResearchBatchRequest, DeepResearchBatchDetail, DeepResearchBatchSummary, DeepResearchPreviewRequest, JobStatus } from '../../../shared/types.js'; import { listPostalAreasByPropagation, findPostalAreaContainingPoint } from '../postal/repository.js'; import { previewDeepResearchForPoint } from '../postal/service.js'; import { runSearchForPostalArea } from '../search/run-search.js'; import { createDeepResearchBatch, failDeepResearchBatch, finalizeDeepResearchBatch, getDeepResearchBatchDetailForUser, listDeepResearchBatchesForUser, } from './repository.js'; function toRadiusKm(searchRadiusMeters: number | null) { return Math.min(50, Math.max(1, Math.ceil((searchRadiusMeters ?? 1000) / 1000))); } export async function listDeepResearchBatches(db: Pool, userId: string): Promise { return listDeepResearchBatchesForUser(db, userId); } export async function getDeepResearchBatchDetail(db: Pool, userId: string, batchId: string): Promise { return getDeepResearchBatchDetailForUser(db, userId, batchId); } export async function createDeepResearchBatchForUser( db: Pool, userId: string, input: CreateDeepResearchBatchRequest, ): Promise { const preview = await previewDeepResearchForPoint(db, input as DeepResearchPreviewRequest); const baseArea = await findPostalAreaContainingPoint(db, input.lat, input.lng); if (!baseArea) { throw new Error('No supported postal area was found for the selected pin.'); } const areaRows = await listPostalAreasByPropagation(db, baseArea.id, input.propagation); const batchId = await createDeepResearchBatch(db, userId, { pinLat: input.lat, pinLng: input.lng, basePostalCode: preview.baseArea.postalCode, countryCode: preview.countryCode, propagation: input.propagation, businessType: input.businessType, keywords: input.keywords, totalPostalAreas: preview.totalAreas, }); let totalResults = 0; let hadFailures = false; try { for (const area of areaRows) { if (typeof area.centroid_lat !== 'number' || typeof area.centroid_lng !== 'number') { hadFailures = true; continue; } try { const result = await runSearchForPostalArea(db, userId, { name: `${input.businessType} in ${area.display_name || area.postal_code}`, city: area.display_name || area.postal_code, address: area.display_name || area.postal_code, postalCode: area.postal_code, countryCode: area.country_code, radiusKm: toRadiusKm(area.search_radius_m), businessType: input.businessType, keywords: input.keywords, lat: area.centroid_lat, lng: area.centroid_lng, deepResearchBatchId: batchId, postalAreaId: area.id, queryContextTerms: [area.postal_code, area.country_code], }); totalResults += result.totalResults; } catch { hadFailures = true; } } const finalStatus: JobStatus = hadFailures ? 'failed' : 'completed'; await finalizeDeepResearchBatch(db, batchId, { status: finalStatus, totalResults, }); } catch (error) { await failDeepResearchBatch(db, batchId); throw error; } const detail = await getDeepResearchBatchDetailForUser(db, userId, batchId); if (!detail) { throw new Error('Failed to load the created deep research batch.'); } return detail; }