import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http' import type { OceanBookingActionRequest, OceanBookingChangeRequest, OceanBookingDetail, OceanBookingPage, OceanBookingQuery, OceanBookingResource, OceanBookingSaveRequest, OceanBookingStatus, OceanBookingReleaseRequest } from '../types' const basePath = '/platform/v1/ocean-bookings' export interface OceanLogisticsChannel { channelCode: string channelName: string } function pageNumber(value: number | string, fallback: number) { const n = Number(value); return Number.isFinite(n) ? Math.trunc(n) : fallback } function booking(raw: OceanBookingResource): OceanBookingResource { const item = raw as OceanBookingResource & { version?: number | string | null } return { ...raw, id: String(raw.id), version: Number(item.version ?? 0), customerId: raw.customerId == null ? null : String(raw.customerId), shippingCompanyId: raw.shippingCompanyId == null ? null : String(raw.shippingCompanyId), bookingAgentId: raw.bookingAgentId == null ? null : String(raw.bookingAgentId), pol: raw.pol ?? raw.departurePortName ?? null, pod: raw.pod ?? raw.destinationPortName ?? null, carrierName: raw.carrierName ?? raw.shippingCompanyName ?? null, bookingAgent: raw.bookingAgent ?? raw.bookingAgentName ?? null, etd: raw.etd ?? raw.estimatedSailingAt ?? null, eta: raw.eta ?? raw.estimatedArrivalAt ?? null, containers: (raw.containers || []).map((container) => ({ ...container, id: String(container.id ?? '') })), } } function detail(raw: OceanBookingDetail): OceanBookingDetail { const source = booking(raw.oceanBooking || raw) const histories = (raw.histories || raw.statusHistory || []).map((event) => ({ ...event, id: String(event.id) })) return { ...raw, ...source, oceanBooking: source, parties: (raw.parties || []).map((party) => ({ ...party, id: party.id == null ? undefined : String(party.id) })), containerRequirements: (raw.containerRequirements || []).map((container) => ({ ...container, id: container.id == null ? undefined : String(container.id) })), containerAllocations: raw.containerAllocations || [], histories, statusHistory: histories, } } export async function listOceanBookings(query: OceanBookingQuery) { const response = await http.get, ApiEnvelope>(basePath, { params: query }) const result = unwrapApiResponse(response) return { ...result, records: (result.records || []).map(booking), page: pageNumber(result.page, query.page), size: pageNumber(result.size, query.size), total: pageNumber(result.total, 0), pages: pageNumber(result.pages, 0) } satisfies OceanBookingPage } export async function getOceanBooking(id: string) { const response = await http.get, ApiEnvelope>(`${basePath}/${encodeURIComponent(id)}`); return detail(unwrapApiResponse(response)) } function idempotencyKey() { return `ocean-booking-${Date.now()}-${Math.random().toString(36).slice(2)}` } export async function createOceanBooking(payload: OceanBookingSaveRequest) { const response = await http.post, ApiEnvelope>(basePath, payload, { headers: { 'Idempotency-Key': idempotencyKey() } }); return detail(unwrapApiResponse(response)) } export async function updateOceanBooking(id: string, payload: OceanBookingSaveRequest) { const response = await http.put, ApiEnvelope>(`${basePath}/${encodeURIComponent(id)}`, payload, { headers: { 'Idempotency-Key': idempotencyKey() } }); return detail(unwrapApiResponse(response)) } export type OceanBookingAction = 'submit' | 'confirm' | 'release' | 'fail' | 'cancel' | 'change-request' | 'change-confirm' | 'change-fail' export async function performOceanBookingAction(id: string, action: Exclude, request: OceanBookingActionRequest) { const response = await http.post, ApiEnvelope>(`${basePath}/${encodeURIComponent(id)}/${action}`, request, { headers: { 'Idempotency-Key': idempotencyKey() } }); return detail(unwrapApiResponse(response)) } export async function releaseOceanBooking(id: string, request: OceanBookingReleaseRequest) { const response = await http.post, ApiEnvelope>(`${basePath}/${encodeURIComponent(id)}/release`, request, { headers: { 'Idempotency-Key': idempotencyKey() } }); return detail(unwrapApiResponse(response)) } export async function requestOceanBookingChange(id: string, request: OceanBookingChangeRequest) { const response = await http.post, ApiEnvelope>(`${basePath}/${encodeURIComponent(id)}/change-request`, request, { headers: { 'Idempotency-Key': idempotencyKey() } }); return detail(unwrapApiResponse(response)) } export async function printOceanBooking(id: string, type: 'SHIPPING_INSTRUCTION' | 'BILL_CONFIRMATION') { return http.get(`${basePath}/${encodeURIComponent(id)}/print`, { params: { type }, responseType: 'blob' }) } export async function listWarehouseLogisticsChannels(warehouseId: string): Promise { const response = await http.get, ApiEnvelope>(`${basePath}/channels`, { params: { originWarehouseId: warehouseId } }) return (unwrapApiResponse(response) || []).map((item) => ({ channelCode: String(item.channelCode), channelName: String(item.channelName) })) } export type { OceanBookingResource, OceanBookingStatus }