import crypto from 'crypto'; /** * Vývojový simulátor sledování objednávek Bolt Food. * * Drží in-memory registr „simulovaných" objednávek klíčovaný tokenem. Funkce * pollBoltOrder v trackingProviders.ts se na začátku podívá, zda je token simulovaný, * a pokud ano, vrátí vyfabrikovaný stav místo dotazu na reálné Bolt API. * * Registr se plní výhradně přes dev endpointy (gated requireDevMode), takže * v produkci zůstává prázdný a chování pollBoltOrder se nijak nemění. * * Postup stavů je řízen ručně (krokováním), bez časové osy — viz advance/setState. */ /** Jeden krok simulace — odpovídá tomu, co vrací Bolt API a co čte OrderProgress. */ export interface SimStep { order_state: string; courier_state?: string; etaSeconds?: number; } /** Tvar objednávky, který vrací pollBoltOrder (shodný s interface BoltOrder). */ export interface SimulatedBoltOrder { order_id: number; order_state: string; expected_time_to_client_in_seconds?: number; courier?: { state?: string } | null; } interface Simulation { groupId: string; token: string; orderId: number; steps: SimStep[]; index: number; /** Jednorázové ruční přepsání aktuálního stavu (má přednost před steps[index]). */ override?: SimStep; } /** * Výchozí scénář „happy path". Stavy a stavy kurýra odpovídají mapování ve * client/src/components/OrderProgress.tsx * (Přijato → Příprava → Vyzvedávání → Na cestě → Doručeno). */ export const DEFAULT_SCENARIO: SimStep[] = [ { order_state: 'waiting_acceptance', etaSeconds: 2100 }, { order_state: 'accepted', etaSeconds: 1800 }, { order_state: 'waiting_preparation', courier_state: 'accepted', etaSeconds: 1800 }, { order_state: 'preparing', courier_state: 'heading_to_provider', etaSeconds: 1500 }, { order_state: 'waiting_delivery', courier_state: 'arrived_to_provider', etaSeconds: 900 }, { order_state: 'in_delivery', courier_state: 'heading_to_client', etaSeconds: 300 }, { order_state: 'delivered', courier_state: 'delivered', etaSeconds: 0 }, ]; const byToken = new Map(); function findByGroup(groupId: string): Simulation | undefined { for (const sim of byToken.values()) { if (sim.groupId === groupId) return sim; } return undefined; } /** Sestaví Bolt objednávku z aktuálního kroku simulace. */ function buildOrder(sim: Simulation): SimulatedBoltOrder { const step = sim.override ?? sim.steps[sim.index]; return { order_id: sim.orderId, order_state: step.order_state, expected_time_to_client_in_seconds: step.etaSeconds, courier: step.courier_state ? { state: step.courier_state } : null, }; } /** Je token registrovaný v simulátoru? */ export function isBoltSimulated(token: string): boolean { return byToken.has(token); } /** Vrátí simulovaný stav objednávky pro daný token (nebo null, pokud není simulovaný). */ export function getSimulatedBoltOrder(token: string): SimulatedBoltOrder | null { const sim = byToken.get(token); return sim ? buildOrder(sim) : null; } /** * Spustí novou simulaci pro skupinu. Pokud už pro skupinu simulace běží, nahradí ji. * Vrátí vygenerovaný 64-hex token (validní pro extractBoltToken). */ export function startBoltSimulation(groupId: string, steps: SimStep[] = DEFAULT_SCENARIO): string { stopBoltSimulationByGroup(groupId); const token = crypto.randomBytes(32).toString('hex'); byToken.set(token, { groupId, token, orderId: crypto.randomInt(100000, 999999), steps: steps.length ? steps : DEFAULT_SCENARIO, index: 0, }); return token; } /** Posune simulaci skupiny na další krok (a zruší případný ruční override). Vrátí aktuální krok. */ export function advanceBoltSimulation(groupId: string): SimStep { const sim = findByGroup(groupId); if (!sim) throw new Error('Pro skupinu neběží žádná simulace Bolt'); sim.override = undefined; sim.index = Math.min(sim.index + 1, sim.steps.length - 1); return sim.steps[sim.index]; } /** Nastaví konkrétní stav simulace skupiny (ruční override — užitečné pro edge-case stavy). */ export function setBoltSimulationStep(groupId: string, step: SimStep): SimStep { const sim = findByGroup(groupId); if (!sim) throw new Error('Pro skupinu neběží žádná simulace Bolt'); sim.override = step; return step; } /** Ukončí simulaci pro skupinu. */ export function stopBoltSimulationByGroup(groupId: string): void { const sim = findByGroup(groupId); if (sim) byToken.delete(sim.token); } /** Vrátí přehled simulace skupiny (pro stavové zobrazení v UI). */ export function getBoltSimulation(groupId: string): { token: string; index: number; total: number; current: SimStep } | null { const sim = findByGroup(groupId); if (!sim) return null; return { token: sim.token, index: sim.index, total: sim.steps.length, current: sim.override ?? sim.steps[sim.index], }; }