import apiClient from '../../../shared/services/apiClient'; export interface BookingData { room_id: number; check_in_date: string; check_out_date: string; guest_count: number; notes?: string; payment_method: 'cash' | 'stripe' | 'paypal'; total_price: number; guest_info: { full_name: string; email: string; phone: string; }; services?: Array<{ service_id: number; quantity: number; }>; promotion_code?: string; referral_code?: string; } export interface Booking { id: number; booking_number: string; user_id: number; room_id: number; check_in_date: string; check_out_date: string; guest_count: number; total_price: number; original_price?: number; discount_amount?: number; promotion_code?: string; status: | 'pending' | 'confirmed' | 'cancelled' | 'checked_in' | 'checked_out'; payment_method: 'cash' | 'stripe' | 'paypal'; payment_status: | 'unpaid' | 'paid' | 'refunded'; deposit_paid?: boolean; requires_deposit?: boolean; notes?: string; guest_info?: { full_name: string; email: string; phone: string; }; room?: { id: number; room_number: string; floor: number; status: string; room_type: { id: number; name: string; base_price: number; capacity: number; images?: string[]; }; }; user?: { id: number; name: string; full_name: string; email: string; phone?: string; phone_number?: string; }; payments?: Payment[]; payment_balance?: { total_paid: number; total_price: number; remaining_balance: number; is_fully_paid: boolean; payment_percentage: number; }; createdAt: string; updatedAt: string; } export interface Payment { id: number; booking_id: number; amount: number; payment_method: string; payment_type: 'full' | 'deposit' | 'remaining'; deposit_percentage?: number; payment_status: 'pending' | 'completed' | 'failed' | 'refunded'; transaction_id?: string; payment_date?: string; notes?: string; created_at: string; updated_at: string; } export interface BookingResponse { success: boolean; data: { booking: Booking; }; message?: string; } export interface BookingsResponse { success: boolean; status?: string; data: { bookings: Booking[]; pagination?: { page: number; limit: number; total: number; totalPages: number; }; }; message?: string; } export interface CheckBookingResponse { success: boolean; data: { booking: Booking; }; message?: string; } export const createBooking = async ( bookingData: BookingData ): Promise => { const response = await apiClient.post<{ status?: string; success?: boolean; data?: { booking?: Booking }; message?: string }>( '/bookings', bookingData ); const data = response.data; // Handle both 'status: success' and 'success: true' formats if (!data.data?.booking) { throw new Error('Booking not found'); } return { success: data.status === 'success' || data.success === true, data: { booking: data.data.booking }, message: data.message, }; }; export const getMyBookings = async (): Promise => { const response = await apiClient.get<{ status?: string; success?: boolean; data?: { bookings?: Booking[]; pagination?: { page: number; limit: number; total: number; totalPages: number } }; message?: string }>( '/bookings/me' ); const data = response.data; // Handle both 'status: success' and 'success: true' formats return { success: data.status === 'success' || data.success === true, data: { bookings: data.data?.bookings || [], pagination: data.data?.pagination, }, message: data.message, }; }; export const getBookingById = async ( id: number ): Promise => { const response = await apiClient.get<{ status?: string; success?: boolean; data?: { booking?: Booking }; message?: string }>( `/bookings/${id}` ); const data = response.data; // Handle both 'status: success' and 'success: true' formats if (!data.data?.booking) { throw new Error('Booking not found'); } return { success: data.status === 'success' || data.success === true, data: { booking: data.data.booking }, message: data.message, }; }; export const cancelBooking = async ( id: number ): Promise => { const response = await apiClient.patch<{ status?: string; success?: boolean; data?: { booking?: Booking }; message?: string }>( `/bookings/${id}/cancel` ); const data = response.data; // Handle both 'status: success' and 'success: true' formats if (!data.data?.booking) { throw new Error('Booking not found'); } return { success: data.status === 'success' || data.success === true, data: { booking: data.data.booking }, message: data.message, }; }; export const checkBookingByNumber = async ( bookingNumber: string ): Promise => { const response = await apiClient.get<{ status?: string; success?: boolean; data?: { booking?: Booking }; message?: string }>( `/bookings/check/${bookingNumber}` ); const data = response.data; // Handle both 'status: success' and 'success: true' formats if (!data.data?.booking) { throw new Error('Booking not found'); } return { success: data.status === 'success' || data.success === true, data: { booking: data.data.booking }, message: data.message, }; }; export const getAllBookings = async ( params?: { status?: string; search?: string; page?: number; limit?: number; startDate?: string; endDate?: string; } ): Promise => { const response = await apiClient.get<{ status?: string; success?: boolean; data?: { bookings?: Booking[]; pagination?: { page: number; limit: number; total: number; totalPages: number } }; message?: string }>('/bookings', { params }); const data = response.data; // Handle both 'status: success' and 'success: true' formats return { success: data.status === 'success' || data.success === true, data: { bookings: data.data?.bookings || [], pagination: data.data?.pagination, }, message: data.message, }; }; export const updateBooking = async ( id: number, data: Partial ): Promise => { const response = await apiClient.put<{ status?: string; success?: boolean; data?: { booking?: Booking }; message?: string }>(`/bookings/${id}`, data); const responseData = response.data; // Handle both 'status: success' and 'success: true' formats if (!responseData.data?.booking) { throw new Error('Booking update failed'); } return { success: responseData.status === 'success' || responseData.success === true, data: { booking: responseData.data.booking }, message: responseData.message, }; }; export const checkRoomAvailability = async ( roomId: number, checkInDate: string, checkOutDate: string ): Promise<{ available: boolean; message?: string }> => { try { const response = await apiClient.get( '/rooms/available', { params: { roomId, from: checkInDate, to: checkOutDate, }, } ); if (response.data?.data?.available !== undefined) { return { available: response.data.data.available, message: response.data.data.message, }; } return { available: true, message: response.data?.message || 'Room is available', }; } catch (error: unknown) { // Type guard for API errors const isApiError = (e: unknown): e is { response?: { status?: number; data?: { message?: string; detail?: string } } } => { return typeof e === 'object' && e !== null; }; if (isApiError(error) && (error.response?.status === 409 || error.response?.status === 404)) { return { available: false, message: error.response.data?.message || error.response.data?.detail || 'Room already booked during this time', }; } throw error; } }; export const notifyPayment = async ( bookingId: number, file?: File ): Promise<{ success: boolean; message?: string }> => { const formData = new FormData(); formData.append('bookingId', bookingId.toString()); if (file) { formData.append('receipt', file); } const response = await apiClient.post( '/notify/payment', formData ); return response.data; }; export const generateQRCode = ( bookingNumber: string, amount: number, bankCode?: string, accountNumber?: string, accountName?: string ): string => { // If bank details are not provided, return empty string (QR code won't be generated) if (!bankCode || !accountNumber || !accountName) { return ''; } const transferContent = bookingNumber; // Generate QR code URL (using generic QR code service, not Vietnam-specific) // Note: This is a placeholder - you may want to use a different QR code service // that supports international bank transfers const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=` + encodeURIComponent(`Bank: ${bankCode}, Account: ${accountNumber}, Name: ${accountName}, Amount: ${amount}, Reference: ${transferContent}`); return qrUrl; }; export const adminCreateBooking = async ( bookingData: BookingData & { user_id: number; status?: string } ): Promise => { const response = await apiClient.post<{ status?: string; success?: boolean; data?: { booking?: Booking }; message?: string }>( '/bookings/admin-create', bookingData ); const data = response.data; // Handle both 'status: success' and 'success: true' formats if (!data.data?.booking) { throw new Error('Booking not found'); } return { success: data.status === 'success' || data.success === true, data: { booking: data.data.booking }, message: data.message, }; }; export default { createBooking, getMyBookings, getBookingById, cancelBooking, checkBookingByNumber, checkRoomAvailability, notifyPayment, generateQRCode, getAllBookings, updateBooking, adminCreateBooking, };