import apiClient from '../api/client'; const STAGE_SEQUENCE = [ { key: 'queued', label: 'Preparing scrape', note: 'Setting up the onboarding job.' }, { key: 'crawling', label: 'Scraping brand pages', note: 'Capturing the homepage, about page, and representative product pages.' }, { key: 'summarizing', label: 'Building site summary', note: 'Condensing the scraped pages into a compact brand summary.' }, { key: 'parsing_brand', label: 'Extracting brand context', note: 'Inferring tone, taglines, and visual signals.' }, { key: 'finalizing_business', label: 'Finalizing business', note: 'Saving the captured data and storefront assets.' }, { key: 'completed', label: 'Completed', note: 'The onboarding job finished successfully.' }, { key: 'failed', label: 'Failed', note: 'The onboarding job could not be completed.' }, ]; const JOB_LOOKUP_RETRY_LIMIT = 4; const RETRYABLE_JOB_NOT_FOUND_WINDOW_MS = 20_000; const MAX_RETRYABLE_JOB_NOT_FOUND_MISSES = 4; export async function startBusinessOnboardingJob(payload) { const response = await apiClient.post('/api/businesses', payload); if (response.status !== 202 || !response.data?.jobId) { throw new Error('Unexpected onboarding response'); } return response.data; } export async function fetchBusinessOnboardingJob(jobId) { const response = await apiClient.get(`/api/businesses/jobs/${encodeURIComponent(jobId)}`); return response.data; } export function getBusinessOnboardingStageMeta(stage) { const normalizedStage = String(stage || '').trim().toLowerCase(); const found = STAGE_SEQUENCE.find((entry) => entry.key === normalizedStage) || STAGE_SEQUENCE[0]; const index = STAGE_SEQUENCE.findIndex((entry) => entry.key === found.key); const percent = normalizedStage === 'completed' ? 100 : normalizedStage === 'failed' ? 100 : Math.max(8, Math.min(92, index <= 0 ? 12 : 12 + (index * 20))); return { stage: found.key, label: found.label, note: found.note, index, percent, totalStages: STAGE_SEQUENCE.length - 2, }; } export function getBusinessOnboardingProgress(job = {}) { const progress = job?.progress && typeof job.progress === 'object' ? job.progress : {}; return { pagesProcessed: Number(progress.pagesProcessed || 0), pagesDiscovered: Number(progress.pagesDiscovered || 0), creditsUsed: Number(progress.creditsUsed || 0), representativePages: Number(progress.representativePages || 0), imageCount: Number(progress.imageCount || 0), linkCount: Number(progress.linkCount || 0), }; } export function getBusinessOnboardingError(job = {}) { const error = job?.error; if (!error) return ''; if (typeof error === 'string') return error; if (typeof error === 'object') { return String(error.message || error.error || error.details || '').trim(); } return String(error).trim(); } export function isRetryableOnboardingJobLookupError(error) { if (error?.response?.status !== 404) return false; const message = String(error?.response?.data?.error || error?.message || '').trim(); return /onboarding job not found/i.test(message); } export function shouldRetryOnboardingJobLookup(error, missCount = 0) { return isRetryableOnboardingJobLookupError(error) && missCount < JOB_LOOKUP_RETRY_LIMIT; } export function getOnboardingJobRetryDelay(missCount = 0) { return Math.min(4500, 1500 + (Math.max(0, missCount) * 700)); } export function shouldRetryMissingBusinessOnboardingJob(job = {}, error, missCount = 0) { if (error?.response?.status !== 404) return false; if (missCount >= MAX_RETRYABLE_JOB_NOT_FOUND_MISSES) return false; const createdAtMs = Date.parse(String(job?.createdAt || '').trim()); if (!Number.isFinite(createdAtMs)) return false; return Date.now() - createdAtMs <= RETRYABLE_JOB_NOT_FOUND_WINDOW_MS; }