import { sleep } from '@/utils'; import { ApiError } from '@/lib/api/errors'; import { KNOWN_MISMATCH_IBAN } from '../constants'; import { normalizeSheba, shebaBankName } from '../iban'; import type { AddBankAccountInput, NurseBankAccountDto, NurseBankAccountsApi } from '../types'; const MOCK_LATENCY_MS = 400; // The number of list reads the inquiry stays pending before resolving. 2 lets the pending // panel show on the invalidation-triggered read, then flip on the next poll — so the // pending→verified/mismatch transition is visible without a manual reload. const POLLS_BEFORE_RESOLVE = 2; interface StoredAccount { dto: NurseBankAccountDto; iban: string; // normalized full value — mock-only; the real value is encrypted server-side pollsLeft: number; } let store: StoredAccount[] = []; let nextId = 1; function maskIban(normalized: string): string { return `••••${normalized.slice(-4)}`; } // Deterministic fake استعلام شبا: every IBAN matches except the configured mismatch IBAN. function resolveIfDue(entry: StoredAccount): void { if (entry.dto.matchedNationalId !== null || entry.pollsLeft <= 0) return; entry.pollsLeft -= 1; if (entry.pollsLeft > 0) return; const matched = normalizeSheba(entry.iban) !== normalizeSheba(KNOWN_MISMATCH_IBAN); entry.dto = { ...entry.dto, matchedNationalId: matched, isVerified: matched }; } /** * In-memory mock behind the NurseBankAccountsApi seam. Drives the pending→verified/mismatch * transition and single-primary enforcement so all three UI states are demonstrable. Mirrors * the real shapes (masked IBAN, `matchedNationalId` gate) for a one-line swap. */ export const nurseBankMockApi: NurseBankAccountsApi = { list: async (): Promise => { await sleep(MOCK_LATENCY_MS); store.forEach(resolveIfDue); return store.map((entry) => entry.dto); }, add: async (input: AddBankAccountInput): Promise => { await sleep(MOCK_LATENCY_MS); const normalized = normalizeSheba(input.iban); if (store.some((entry) => entry.iban === normalized)) { throw new ApiError(400, 'Duplicate IBAN', 'iban_duplicate'); } const dto: NurseBankAccountDto = { id: nextId++, bankName: shebaBankName(normalized), ibanMasked: maskIban(normalized), isPrimary: store.length === 0, isVerified: false, matchedNationalId: null, }; store = [...store, { dto, iban: normalized, pollsLeft: POLLS_BEFORE_RESOLVE }]; return dto; }, setPrimary: async (id: number): Promise => { await sleep(MOCK_LATENCY_MS); if (!store.some((entry) => entry.dto.id === id)) throw new ApiError(404, 'Account not found'); store = store.map((entry) => ({ ...entry, dto: { ...entry.dto, isPrimary: entry.dto.id === id } })); }, verifyOwnership: async (id: number): Promise => { await sleep(MOCK_LATENCY_MS); const entry = store.find((item) => item.dto.id === id); if (!entry) throw new ApiError(404, 'Account not found'); const matched = normalizeSheba(entry.iban) !== normalizeSheba(KNOWN_MISMATCH_IBAN); entry.dto = { ...entry.dto, matchedNationalId: matched, isVerified: matched }; entry.pollsLeft = 0; return entry.dto; }, };