jest.mock('../../src/interceptor/apiClient');

import { CreateBookingPayload } from '@/redux/types/bookingType';
import apiClient from '../../src/interceptor/apiClient';
import { API_ENDPOINTS } from '@/src/utils/commonVariables';

const mockedApi = apiClient as jest.Mocked<typeof apiClient>;

describe('createBooking thunk', () => {
    const mockDispatch = jest.fn();
    const mockCallback = jest.fn();

    /**
     * Asynchronous thunk action for creating a booking.
     * Dispatches actions that represent the different states of the API call: request, success, or failure.
     * 
     * @param payload - An object containing the booking data to be sent to the API.
     * @param callback - An optional callback function that receives the response data from the API if the call is successful.
     * 
     * Dispatches:
     * - REQUEST_CREATE_BOOKING: Before the API call to indicate loading.
     * - SUCCESS_CREATE_BOOKING: On successful API response with the booking data.
     * - FAILURE_CREATE_BOOKING: In case of an API error.
     */
    const createBooking =
        (
            payload: CreateBookingPayload,
            callback?: (data: any) => void
        ) =>
            async (dispatch: any) => {
                try {
                    dispatch({ type: 'REQUEST_CREATE_BOOKING' });
                    const response: any = await apiClient.post(API_ENDPOINTS.CREATE_BOOKING, payload);

                    if (response?.data?.meta?.status === 0) {
                        // Normally a toast, but skipped in this version
                    } else {
                        callback && callback(response?.data);
                    }

                    dispatch({
                        type: 'SUCCESS_CREATE_BOOKING',
                        payload: response?.data,
                    });
                } catch (error: any) {
                    dispatch({ type: 'FAILURE_CREATE_BOOKING' });
                    // Skipping toast here as well
                }
            };

    const payload: CreateBookingPayload = {
        propertyId: 'property123',
        checkInDate: '2025-08-01',
        checkOutDate: '2025-08-05',
        numberOfGuest: 2,
        skipPayment: true,
        confirmBooking: false,
        isGuestBooking: true,
    };

    const mockSuccessResponse = {
        data: {
            meta: {
                status: 1,
                message: 'Booking created successfully',
            },
            data: {
                bookingId: 'booking-456',
                status: 'confirmed',
            },
        },
    };

    const mockHandledErrorResponse = {
        data: {
            meta: {
                status: 0,
                message: 'Booking failed validation',
            },
        },
    };

    beforeEach(() => {
        jest.clearAllMocks();
    });

    // ✅ meta.status === 1 → success
    it('should dispatch success and call callback when booking is successful', async () => {
        mockedApi.post.mockResolvedValueOnce(mockSuccessResponse);

        await createBooking(payload, mockCallback)(mockDispatch);

        expect(mockDispatch).toHaveBeenCalledWith({ type: 'REQUEST_CREATE_BOOKING' });
        expect(mockedApi.post).toHaveBeenCalledWith(API_ENDPOINTS.CREATE_BOOKING, payload);
        expect(mockCallback).toHaveBeenCalledWith(mockSuccessResponse.data);
        expect(mockDispatch).toHaveBeenCalledWith({
            type: 'SUCCESS_CREATE_BOOKING',
            payload: mockSuccessResponse.data,
        });
    });

    // ❌ meta.status === 0 → handled error, no callback
    it('should dispatch success without calling callback when meta.status is 0', async () => {
        mockedApi.post.mockResolvedValueOnce(mockHandledErrorResponse);

        await createBooking(payload, mockCallback)(mockDispatch);

        expect(mockDispatch).toHaveBeenCalledWith({ type: 'REQUEST_CREATE_BOOKING' });
        expect(mockCallback).not.toHaveBeenCalled();
        expect(mockDispatch).toHaveBeenCalledWith({
            type: 'SUCCESS_CREATE_BOOKING',
            payload: mockHandledErrorResponse.data,
        });
    });

    // ❌ Exception → dispatch failure
    it('should dispatch failure on API error', async () => {
        mockedApi.post.mockRejectedValueOnce(new Error('Network error'));

        await createBooking(payload, mockCallback)(mockDispatch);

        expect(mockDispatch).toHaveBeenCalledWith({ type: 'REQUEST_CREATE_BOOKING' });
        expect(mockDispatch).toHaveBeenCalledWith({ type: 'FAILURE_CREATE_BOOKING' });
        expect(mockCallback).not.toHaveBeenCalled();
    });

    // ✅ New payload test: includes bookingId, guestDetails, and extra fields
    it('should handle extended payload with guest details and call callback on success', async () => {
        const extendedPayload: CreateBookingPayload = {
            bookingId: 'booking-123',
            propertyId: 'property-456',
            isGuestBooking: false,
            checkInDate: '2025-08-01',
            checkOutDate: '2025-08-05',
            numberOfGuest: 3,
            skipPayment: true,
            confirmBooking: false,
            guestDetails: {
                firstName: 'John',
                lastName: 'Doe',
                email: 'john.doe@example.com',
                phoneNumber: '+1234567890',
                dateOfBirth: '1990-01-01',
                nationality: 'Austrian',
                estimatedTimeOfArrival: '12:00 PM',
                countryId: 'country-abc',
            },
            agreedToHouseRules: true,
            infoForHost: {
                specialRequest: 'Please leave the key under the mat.',
            },
        };

        const responseWithExtras = {
            data: {
                meta: {
                    status: 1,
                    message: 'Booking created with guest details',
                },
                data: {
                    bookingId: 'booking-123',
                    status: 'confirmed',
                },
            },
        };

        mockedApi.post.mockResolvedValueOnce(responseWithExtras);

        await createBooking(extendedPayload, mockCallback)(mockDispatch);

        expect(mockDispatch).toHaveBeenCalledWith({ type: 'REQUEST_CREATE_BOOKING' });
        expect(mockedApi.post).toHaveBeenCalledWith(API_ENDPOINTS.CREATE_BOOKING, extendedPayload);
        expect(mockCallback).toHaveBeenCalledWith(responseWithExtras.data);
        expect(mockDispatch).toHaveBeenCalledWith({
            type: 'SUCCESS_CREATE_BOOKING',
            payload: responseWithExtras.data,
        });
    });

});
