import { initContract } from '@ts-rest/core'; import { z } from 'zod'; import { syncPushRequestSchema, syncPushResponseSchema, syncPullQuerySchema, syncPullResponseSchema, } from '../schemas/shopkeeper-sync.schemas'; import { errorResponseSchema } from '../schemas/common.schemas'; const c = initContract(); export const shopkeeperSyncContract = c.router({ pushChanges: { method: 'POST', path: '/shopkeeper/sync/push', body: syncPushRequestSchema, responses: { 200: syncPushResponseSchema, 400: errorResponseSchema, 401: errorResponseSchema, 404: errorResponseSchema, }, summary: 'Device pushes local changes to server', }, pullChanges: { method: 'GET', path: '/shopkeeper/sync/pull', query: syncPullQuerySchema, responses: { 200: syncPullResponseSchema, 400: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Device pulls server changes since timestamp', }, getSyncStatus: { method: 'GET', path: '/shopkeeper/sync/status', query: z.object({ shopId: z.string().uuid(), deviceId: z.string() }), responses: { 200: z.object({ lastSyncTimestamp: z.number().nullable(), pendingCount: z.number().int(), generationId: z.number().int(), }), 401: errorResponseSchema, }, summary: 'Check last sync timestamp and pending count', }, listConflicts: { method: 'GET', path: '/shopkeeper/sync/conflicts', query: z.object({ resolved: z.enum(['true', 'false']).optional(), limit: z.coerce.number().int().min(1).max(100).optional(), }), responses: { 200: z.object({ conflicts: z.array( z.object({ id: z.string().uuid(), recordId: z.string().uuid(), table: z.string(), serverVersion: z.record(z.unknown()), resolution: z.string(), deviceId: z.string().nullable(), resolvedAt: z.coerce.date().nullable(), createdAt: z.coerce.date(), }), ), total: z.number().int(), }), 401: errorResponseSchema, }, summary: 'List sync conflicts for the shop (unresolved or all)', }, resolveConflict: { method: 'PATCH', path: '/shopkeeper/sync/conflicts/:id', pathParams: z.object({ id: z.string().uuid() }), body: z.object({ resolution: z.enum(['keep_local', 'use_cloud']), }), responses: { 200: z.object({ ok: z.literal(true) }), 404: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Mark a sync conflict as resolved (keep_local or use_cloud)', }, });