import { reservationTaskAudits, reservationTaskDetails, } from '@/fixtures/reservation' import { getJson, sendJson } from '@/services/httpClient' import type { ReservationOperaOperationResult, ReservationOperaSimulationRequest, ReservationOrderDetailResult, ReservationOrderListFilters, ReservationOrderListResult, ReservationTaskAuditListResult, ReservationTaskDetailResult, ReservationTaskListFilters, ReservationTaskListResult, ReservationTaskPayloadMutationRequest, ReservationTaskPayloadMutationResult, SourceMessageConversationResult, } from '@/types/reservation' const useReservationFixtures = import.meta.env.VITE_RESERVATION_USE_FIXTURES === 'true' export class EndpointPendingError extends Error { constructor(message: string) { super(message) this.name = 'EndpointPendingError' } } export async function fetchReservationOrders( filters: ReservationOrderListFilters = {}, ): Promise { void filters throw new EndpointPendingError('GET /api/reservation/orders is pending backend implementation.') } export async function fetchReservationTaskList( filters: ReservationTaskListFilters = {}, ): Promise { return getJson(withQuery('/api/reservation/tasks', filters)) } export async function fetchReservationOrderDetail(orderId: string): Promise { return getJson( withQuery(`/api/reservation/orders/${encodeURIComponent(orderId)}`, { include_tasks: true, include_source_summary: true, }), ) } export async function fetchSourceMessageConversation( sourceMessageId: string, ): Promise { throw new EndpointPendingError( `GET /api/source-messages/${sourceMessageId}/conversation is pending backend implementation.`, ) } export async function fetchReservationTaskDetail(taskId: string): Promise { if (useReservationFixtures) { return fixtureTaskDetail(taskId) } return getJson(`/api/reservation/tasks/${taskId}`) } export async function saveReservationTaskDraft( taskId: string, request: ReservationTaskPayloadMutationRequest, ): Promise { if (useReservationFixtures) { return fixtureTaskMutation(taskId, request, false) } return sendJson(`/api/reservation/tasks/${taskId}/draft`, 'PUT', request) } export async function confirmReservationTask( taskId: string, request: ReservationTaskPayloadMutationRequest, ): Promise { if (useReservationFixtures) { return fixtureTaskMutation(taskId, request, true) } return sendJson(`/api/reservation/tasks/${taskId}/confirm`, 'POST', request) } export async function executeReservationOperaOperation( taskId: string, operationId: string, request: ReservationOperaSimulationRequest, ): Promise { if (useReservationFixtures) { return fixtureOperaOperation(taskId, operationId, request, 'execute') } return sendJson( `/api/reservation/tasks/${taskId}/opera-operations/${operationId}/execute`, 'POST', request, ) } export async function retryReservationOperaOperation( taskId: string, operationId: string, request: ReservationOperaSimulationRequest, ): Promise { if (useReservationFixtures) { return fixtureOperaOperation(taskId, operationId, request, 'retry') } return sendJson( `/api/reservation/tasks/${taskId}/opera-operations/${operationId}/retry`, 'POST', request, ) } export async function fetchReservationTaskAudits(taskId: string): Promise { if (useReservationFixtures) { return Promise.resolve( reservationTaskAudits.find((audit) => audit.task_id === taskId) ?? { task_id: taskId, items: [], }, ) } return getJson(`/api/reservation/tasks/${taskId}/audits`) } function withQuery(path: string, params: object): string { const searchParams = new URLSearchParams() Object.entries(params).forEach(([key, value]) => { if ( value === undefined || value === null || value === '' || !['string', 'number', 'boolean'].includes(typeof value) ) { return } searchParams.set(key, String(value)) }) const query = searchParams.toString() return query ? `${path}?${query}` : path } function fixtureTaskDetail(taskId: string): Promise { const detail = reservationTaskDetails.find((item) => item.task_id === taskId) if (!detail) { throw new Error(`Reservation task ${taskId} was not found in frontend fixture data.`) } return Promise.resolve(structuredClone(detail)) } function fixtureTaskMutation( taskId: string, request: ReservationTaskPayloadMutationRequest, confirmed: boolean, ): Promise { return fixtureTaskDetail(taskId).then((detail) => ({ task_id: detail.task_id, order_id: detail.order_id, task_status: confirmed ? 'READY' : detail.task_status, draft_payload: request.field_values, confirmed_payload: confirmed ? request.field_values : detail.confirmed_payload, opera_operations: confirmed ? detail.opera_operations : [], })) } async function fixtureOperaOperation( taskId: string, operationId: string, request: ReservationOperaSimulationRequest, action: 'execute' | 'retry', ): Promise { const detail = await fixtureTaskDetail(taskId) const operation = detail.opera_operations.find((item) => item.operation_id === operationId) if (!operation) { throw new Error(`Reservation OPERA operation ${operationId} was not found in frontend fixture data.`) } const success = request.simulate_success !== false const attemptNumber = operation.attempt_count + 1 return { ...operation, operation_status: success ? 'SUCCEEDED' : 'FAILED', attempt_count: attemptNumber, last_error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure', attempts: [ ...operation.attempts, { attempt_id: `${operationId}-${action}-${attemptNumber}`, attempt_number: attemptNumber, attempt_status: success ? 'SUCCEEDED' : 'FAILED', response_payload: success ? { simulated: true } : null, error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure', started_at: new Date().toISOString(), finished_at: new Date().toISOString(), }, ], } }