/**
 * Unit tests for BookingPage component
 *
 * Tests all booking functionality including:
 * - Service selection and rendering
 * - Date/time picker interaction
 * - Multi-step booking flow
 * - Booking confirmation
 * - Loading states
 * - Error states
 * - Complete user flows
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor, within } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import React, { type ReactNode } from 'react';
import BookingPage from '../BookingPage';
import { useServices } from '../../../hooks/useServices';
import { useLocations } from '../../../hooks/useLocations';
import { User, Business, Service, Location } from '../../../types';

// Mock the useServices hook
vi.mock('../../../hooks/useServices', () => ({
  useServices: vi.fn(),
}));

// Mock the useLocations hook
vi.mock('../../../hooks/useLocations', () => ({
  useLocations: vi.fn(),
}));

// Mock lucide-react icons to avoid rendering issues in tests
vi.mock('lucide-react', () => ({
  Check: () => <div data-testid="check-icon">Check</div>,
  ChevronLeft: () => <div data-testid="chevron-left-icon">ChevronLeft</div>,
  Calendar: () => <div data-testid="calendar-icon">Calendar</div>,
  Clock: () => <div data-testid="clock-icon">Clock</div>,
  AlertTriangle: () => <div data-testid="alert-icon">AlertTriangle</div>,
  CreditCard: () => <div data-testid="credit-card-icon">CreditCard</div>,
  Loader2: () => <div data-testid="loader-icon">Loader2</div>,
  MapPin: () => <div data-testid="map-pin-icon">MapPin</div>,
}));

// Mock react-router-dom's useOutletContext
let mockOutletContext: { user: User; business: Business } | null = null;

vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useOutletContext: () => mockOutletContext,
  };
});

// Test data factories
const createMockUser = (overrides?: Partial<User>): User => ({
  id: '1',
  name: 'John Doe',
  email: 'john@example.com',
  role: 'customer',
  ...overrides,
});

const createMockBusiness = (overrides?: Partial<Business>): Business => ({
  id: '1',
  name: 'Test Business',
  subdomain: 'testbiz',
  primaryColor: '#3B82F6',
  secondaryColor: '#10B981',
  whitelabelEnabled: false,
  paymentsEnabled: true,
  requirePaymentMethodToBook: false,
  cancellationWindowHours: 24,
  lateCancellationFeePercent: 50,
  ...overrides,
});

const createMockService = (overrides?: Partial<Service>): Service => ({
  id: '1',
  name: 'Haircut',
  durationMinutes: 60,
  price: 50.0,
  description: 'Professional haircut service',
  displayOrder: 0,
  photos: [],
  ...overrides,
});

const createMockLocation = (overrides?: Partial<Location>): Location => ({
  id: 1,
  name: 'Main Location',
  is_active: true,
  is_primary: true,
  display_order: 0,
  ...overrides,
});

// Test wrapper with all necessary providers
const createWrapper = (queryClient: QueryClient, user: User, business: Business) => {
  return ({ children }: { children: ReactNode }) => (
    <QueryClientProvider client={queryClient}>
      <MemoryRouter initialEntries={['/book']}>
        <Routes>
          <Route
            path="/book"
            element={
              <div>
                {React.cloneElement(children as React.ReactElement, {
                  // Simulate useOutletContext
                })}
              </div>
            }
          />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>
  );
};

// Custom render function with context
const renderBookingPage = (user: User, business: Business, queryClient: QueryClient) => {
  // Set the mock outlet context before rendering
  mockOutletContext = { user, business };

  return render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter>
        <BookingPage />
      </MemoryRouter>
    </QueryClientProvider>
  );
};

describe('BookingPage', () => {
  let queryClient: QueryClient;
  let mockUser: User;
  let mockBusiness: Business;

  beforeEach(() => {
    queryClient = new QueryClient({
      defaultOptions: {
        queries: { retry: false, gcTime: 0 },
        mutations: { retry: false },
      },
    });
    mockUser = createMockUser();
    mockBusiness = createMockBusiness();
    vi.clearAllMocks();

    // Default: single location (skips location step)
    vi.mocked(useLocations).mockReturnValue({
      data: [createMockLocation()],
      isLoading: false,
      isSuccess: true,
      isError: false,
      error: null,
    } as any);
  });

  afterEach(() => {
    queryClient.clear();
  });

  describe('Service Selection (Step 1)', () => {
    it('should render loading state while fetching services', () => {
      vi.mocked(useServices).mockReturnValue({
        data: undefined,
        isLoading: true,
        isSuccess: false,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByTestId('loader-icon')).toBeInTheDocument();
      expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
    });

    it('should render empty state when no services available', () => {
      vi.mocked(useServices).mockReturnValue({
        data: [],
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      // With single location, message includes location name
      expect(screen.getByText(/No services available/)).toBeInTheDocument();
      expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
    });

    it('should render list of available services', () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
        createMockService({ id: '2', name: 'Hair Color', price: 120, durationMinutes: 120 }),
        createMockService({ id: '3', name: 'Styling', price: 40, durationMinutes: 45 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Haircut')).toBeInTheDocument();
      expect(screen.getByText('Hair Color')).toBeInTheDocument();
      expect(screen.getByText('Styling')).toBeInTheDocument();
      expect(screen.getByText('$50.00')).toBeInTheDocument();
      expect(screen.getByText('$120.00')).toBeInTheDocument();
      expect(screen.getByText('$40.00')).toBeInTheDocument();
    });

    it('should display service details including duration and description', () => {
      const mockServices = [
        createMockService({
          id: '1',
          name: 'Deep Tissue Massage',
          price: 90,
          durationMinutes: 90,
          description: 'Relaxing full-body massage',
        }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Deep Tissue Massage')).toBeInTheDocument();
      expect(screen.getByText(/90 min.*Relaxing full-body massage/)).toBeInTheDocument();
      expect(screen.getByText('$90.00')).toBeInTheDocument();
    });

    it('should advance to step 2 when a service is selected', async () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      const serviceButton = screen.getByRole('button', { name: /Haircut/i });
      fireEvent.click(serviceButton);

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });
    });

    it('should show correct subtitle for step 1', () => {
      vi.mocked(useServices).mockReturnValue({
        data: [],
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Pick from our list of available services.')).toBeInTheDocument();
    });
  });

  describe('Time Selection (Step 2)', () => {
    beforeEach(() => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);
    });

    it('should display available time slots', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select a service first
      const serviceButton = screen.getByRole('button', { name: /Haircut/i });
      fireEvent.click(serviceButton);

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Check that time slots are displayed
      const timeButtons = screen.getAllByRole('button');
      // Should have multiple time slot buttons
      expect(timeButtons.length).toBeGreaterThan(1);
    });

    it('should show subtitle with current date', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select a service first
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        const todayDate = new Date().toLocaleDateString();
        expect(screen.getByText(new RegExp(`Available times for ${todayDate}`, 'i'))).toBeInTheDocument();
      });
    });

    it('should advance to step 3 when a time is selected', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select a service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Select first available time slot
      const timeButtons = screen.getAllByRole('button').filter(btn =>
        btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
      );

      if (timeButtons.length > 0) {
        fireEvent.click(timeButtons[0]);

        await waitFor(() => {
          expect(screen.getByText('Step 3: Confirm Details')).toBeInTheDocument();
        });
      }
    });

    it('should show back button on step 2', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select a service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByTestId('chevron-left-icon')).toBeInTheDocument();
      });
    });

    it('should go back to step 1 when back button is clicked', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select a service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Click back button
      const backButton = screen.getAllByRole('button').find(btn =>
        btn.querySelector('[data-testid="chevron-left-icon"]')
      );

      if (backButton) {
        fireEvent.click(backButton);

        await waitFor(() => {
          expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
        });
      }
    });
  });

  describe('Booking Confirmation (Step 3)', () => {
    beforeEach(() => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);
    });

    const navigateToStep3 = async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Step 1: Select service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Step 2: Select time
      const timeButtons = screen.getAllByRole('button').filter(btn =>
        btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
      );

      if (timeButtons.length > 0) {
        fireEvent.click(timeButtons[0]);
      }

      await waitFor(() => {
        expect(screen.getByText('Step 3: Confirm Details')).toBeInTheDocument();
      });
    };

    it('should display booking confirmation details', async () => {
      await navigateToStep3();

      expect(screen.getByText('Confirm Your Booking')).toBeInTheDocument();
      expect(screen.getByText(/You are booking/i)).toBeInTheDocument();
      expect(screen.getByText(/Haircut/i)).toBeInTheDocument();
      expect(screen.getByTestId('calendar-icon')).toBeInTheDocument();
    });

    it('should show confirm appointment button', async () => {
      await navigateToStep3();

      const confirmButton = screen.getByRole('button', { name: /Confirm Appointment/i });
      expect(confirmButton).toBeInTheDocument();
    });

    it('should show subtitle with review instructions', async () => {
      await navigateToStep3();

      expect(screen.getByText('Please review your appointment details below.')).toBeInTheDocument();
    });

    it('should show back button on step 3', async () => {
      await navigateToStep3();

      expect(screen.getByTestId('chevron-left-icon')).toBeInTheDocument();
    });

    it('should go back to step 2 when back button is clicked', async () => {
      await navigateToStep3();

      // Click back button
      const backButton = screen.getAllByRole('button').find(btn =>
        btn.querySelector('[data-testid="chevron-left-icon"]')
      );

      if (backButton) {
        fireEvent.click(backButton);

        await waitFor(() => {
          expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
        });
      }
    });

    it('should advance to step 4 when confirm button is clicked', async () => {
      await navigateToStep3();

      const confirmButton = screen.getByRole('button', { name: /Confirm Appointment/i });
      fireEvent.click(confirmButton);

      await waitFor(() => {
        expect(screen.getByText('Booking Confirmed')).toBeInTheDocument();
        expect(screen.getByText('Appointment Booked!')).toBeInTheDocument();
      });
    });
  });

  describe('Booking Success (Step 4)', () => {
    beforeEach(() => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);
    });

    const navigateToStep4 = async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Step 1: Select service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Step 2: Select time
      const timeButtons = screen.getAllByRole('button').filter(btn =>
        btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
      );

      if (timeButtons.length > 0) {
        fireEvent.click(timeButtons[0]);
      }

      await waitFor(() => {
        expect(screen.getByText('Step 3: Confirm Details')).toBeInTheDocument();
      });

      // Step 3: Confirm
      fireEvent.click(screen.getByRole('button', { name: /Confirm Appointment/i }));

      await waitFor(() => {
        expect(screen.getByText('Booking Confirmed')).toBeInTheDocument();
      });
    };

    it('should display success message with check icon', async () => {
      await navigateToStep4();

      expect(screen.getByText('Appointment Booked!')).toBeInTheDocument();
      expect(screen.getByTestId('check-icon')).toBeInTheDocument();
    });

    it('should show booking confirmation details', async () => {
      await navigateToStep4();

      expect(screen.getByText(/Your appointment for/i)).toBeInTheDocument();
      expect(screen.getByText(/Haircut/i)).toBeInTheDocument();
      expect(screen.getByText(/is confirmed/i)).toBeInTheDocument();
    });

    it('should show confirmation email message', async () => {
      await navigateToStep4();

      expect(screen.getByText("We've sent a confirmation to your email.")).toBeInTheDocument();
    });

    it('should show "Go to Dashboard" link', async () => {
      await navigateToStep4();

      const dashboardLink = screen.getByRole('link', { name: /Go to Dashboard/i });
      expect(dashboardLink).toBeInTheDocument();
      expect(dashboardLink).toHaveAttribute('href', '/');
    });

    it('should show "Book Another" button', async () => {
      await navigateToStep4();

      const bookAnotherButton = screen.getByRole('button', { name: /Book Another/i });
      expect(bookAnotherButton).toBeInTheDocument();
    });

    it('should reset flow when "Book Another" is clicked', async () => {
      await navigateToStep4();

      const bookAnotherButton = screen.getByRole('button', { name: /Book Another/i });
      fireEvent.click(bookAnotherButton);

      await waitFor(() => {
        expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
      });
    });

    it('should not show back button on step 4', async () => {
      await navigateToStep4();

      const backButtons = screen.queryAllByTestId('chevron-left-icon');
      expect(backButtons.length).toBe(0);
    });
  });

  describe('Complete User Flow', () => {
    it('should complete entire booking flow from service selection to confirmation', async () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Massage Therapy', price: 80, durationMinutes: 90 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Step 1: User sees and selects service
      expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
      expect(screen.getByText('Massage Therapy')).toBeInTheDocument();
      fireEvent.click(screen.getByRole('button', { name: /Massage Therapy/i }));

      // Step 2: User sees and selects time
      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      const timeButtons = screen.getAllByRole('button').filter(btn =>
        btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
      );
      fireEvent.click(timeButtons[0]);

      // Step 3: User confirms booking
      await waitFor(() => {
        expect(screen.getByText('Step 3: Confirm Details')).toBeInTheDocument();
        expect(screen.getByText(/Massage Therapy/i)).toBeInTheDocument();
      });

      fireEvent.click(screen.getByRole('button', { name: /Confirm Appointment/i }));

      // Step 4: User sees success message
      await waitFor(() => {
        expect(screen.getByText('Booking Confirmed')).toBeInTheDocument();
        expect(screen.getByText('Appointment Booked!')).toBeInTheDocument();
        expect(screen.getByText(/Massage Therapy/i)).toBeInTheDocument();
      });
    });

    it('should allow user to navigate backward through steps', async () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Go to step 2
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));
      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Go back to step 1
      const backButton1 = screen.getAllByRole('button').find(btn =>
        btn.querySelector('[data-testid="chevron-left-icon"]')
      );
      if (backButton1) fireEvent.click(backButton1);

      await waitFor(() => {
        expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
      });

      // Go to step 2 again
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));
      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Go to step 3
      const timeButtons = screen.getAllByRole('button').filter(btn =>
        btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
      );
      if (timeButtons.length > 0) fireEvent.click(timeButtons[0]);

      await waitFor(() => {
        expect(screen.getByText('Step 3: Confirm Details')).toBeInTheDocument();
      });

      // Go back to step 2
      const backButton2 = screen.getAllByRole('button').find(btn =>
        btn.querySelector('[data-testid="chevron-left-icon"]')
      );
      if (backButton2) fireEvent.click(backButton2);

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });
    });
  });

  describe('Edge Cases', () => {
    it('should handle service with zero price', () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Free Consultation', price: 0, durationMinutes: 30 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('$0.00')).toBeInTheDocument();
    });

    it('should handle service with long name', () => {
      const mockServices = [
        createMockService({
          id: '1',
          name: 'Very Long Service Name That Could Potentially Break The Layout',
          price: 100,
          durationMinutes: 120,
        }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Very Long Service Name That Could Potentially Break The Layout')).toBeInTheDocument();
    });

    it('should handle service with long description', () => {
      const longDescription = 'A'.repeat(200);
      const mockServices = [
        createMockService({
          id: '1',
          name: 'Service',
          price: 50,
          durationMinutes: 60,
          description: longDescription,
        }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText(new RegExp(longDescription))).toBeInTheDocument();
    });

    it('should handle multiple services with same price', () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Service A', price: 50, durationMinutes: 60 }),
        createMockService({ id: '2', name: 'Service B', price: 50, durationMinutes: 45 }),
        createMockService({ id: '3', name: 'Service C', price: 50, durationMinutes: 30 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      const priceElements = screen.getAllByText('$50.00');
      expect(priceElements.length).toBe(3);
    });

    it('should handle rapid step navigation', async () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Rapidly click through steps
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        const timeButtons = screen.getAllByRole('button').filter(btn =>
          btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
        );
        if (timeButtons.length > 0) {
          fireEvent.click(timeButtons[0]);
        }
      });

      await waitFor(() => {
        const confirmButton = screen.queryByRole('button', { name: /Confirm Appointment/i });
        if (confirmButton) {
          fireEvent.click(confirmButton);
        }
      });

      // Should end up at success page
      await waitFor(() => {
        expect(screen.getByText('Appointment Booked!')).toBeInTheDocument();
      }, { timeout: 3000 });
    });
  });

  describe('Accessibility', () => {
    it('should have proper heading hierarchy', () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      const heading = screen.getByText('Step 1: Select a Service');
      expect(heading).toBeInTheDocument();
    });

    it('should have clickable service buttons', () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      const serviceButton = screen.getByRole('button', { name: /Haircut/i });
      expect(serviceButton).toBeInTheDocument();
      expect(serviceButton).toBeEnabled();
    });

    it('should have navigable link in success step', async () => {
      const mockServices = [
        createMockService({ id: '1', name: 'Haircut', price: 50, durationMinutes: 60 }),
      ];

      vi.mocked(useServices).mockReturnValue({
        data: mockServices,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Navigate to success page
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        const timeButtons = screen.getAllByRole('button').filter(btn =>
          btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
        );
        if (timeButtons.length > 0) fireEvent.click(timeButtons[0]);
      });

      await waitFor(() => {
        const confirmButton = screen.queryByRole('button', { name: /Confirm Appointment/i });
        if (confirmButton) fireEvent.click(confirmButton);
      });

      await waitFor(() => {
        const dashboardLink = screen.queryByRole('link', { name: /Go to Dashboard/i });
        expect(dashboardLink).toBeInTheDocument();
      });
    });
  });

  describe('Location Selection (Multi-Location)', () => {
    const multipleLocations = [
      createMockLocation({ id: 1, name: 'Downtown Office', is_primary: true }),
      createMockLocation({ id: 2, name: 'Uptown Branch', is_primary: false }),
    ];

    beforeEach(() => {
      // Setup multiple locations
      vi.mocked(useLocations).mockReturnValue({
        data: multipleLocations,
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      // Setup services
      vi.mocked(useServices).mockReturnValue({
        data: [createMockService({ id: '1', name: 'Haircut', price: 50 })],
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);
    });

    it('should show location selection as step 1 when multiple locations exist', () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Step 1: Select a Location')).toBeInTheDocument();
      expect(screen.getByText('Choose your preferred location.')).toBeInTheDocument();
    });

    it('should display all available locations', () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Downtown Office')).toBeInTheDocument();
      expect(screen.getByText('Uptown Branch')).toBeInTheDocument();
    });

    it('should show Primary badge on primary location', () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Primary')).toBeInTheDocument();
    });

    it('should advance to service selection after choosing a location', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Click on a location
      fireEvent.click(screen.getByRole('button', { name: /Downtown Office/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Select a Service')).toBeInTheDocument();
      });
    });

    it('should show selected location indicator after selection', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Click on a location
      fireEvent.click(screen.getByRole('button', { name: /Downtown Office/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Select a Service')).toBeInTheDocument();
        // Location indicator should be visible (check for Change button as indicator)
        expect(screen.getByText('Change')).toBeInTheDocument();
      });
    });

    it('should allow changing location from indicator', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select location
      fireEvent.click(screen.getByRole('button', { name: /Downtown Office/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Select a Service')).toBeInTheDocument();
      });

      // Click change
      fireEvent.click(screen.getByText('Change'));

      await waitFor(() => {
        expect(screen.getByText('Step 1: Select a Location')).toBeInTheDocument();
      });
    });

    it('should include location in confirmation', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Step 0: Select location
      fireEvent.click(screen.getByRole('button', { name: /Downtown Office/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Select a Service')).toBeInTheDocument();
      });

      // Step 1: Select service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 3: Choose a Time')).toBeInTheDocument();
      });

      // Step 2: Select time
      const timeButtons = screen.getAllByRole('button').filter(btn =>
        btn.textContent && /\d{1,2}:\d{2}/.test(btn.textContent)
      );
      if (timeButtons.length > 0) fireEvent.click(timeButtons[0]);

      await waitFor(() => {
        expect(screen.getByText('Step 4: Confirm Details')).toBeInTheDocument();
      });

      // Verify location is shown in confirmation (multiple instances OK)
      const locationTexts = screen.getAllByText('Downtown Office');
      expect(locationTexts.length).toBeGreaterThan(0);
    });
  });

  describe('Single Location (Skip Location Step)', () => {
    beforeEach(() => {
      // Setup single location
      vi.mocked(useLocations).mockReturnValue({
        data: [createMockLocation({ id: 1, name: 'Main Office' })],
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      // Setup services
      vi.mocked(useServices).mockReturnValue({
        data: [createMockService({ id: '1', name: 'Haircut', price: 50 })],
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);
    });

    it('should skip location step and start at service selection', () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
      expect(screen.queryByText('Select a Location')).not.toBeInTheDocument();
    });

    it('should not show location change option', async () => {
      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Select service
      fireEvent.click(screen.getByRole('button', { name: /Haircut/i }));

      await waitFor(() => {
        expect(screen.getByText('Step 2: Choose a Time')).toBeInTheDocument();
      });

      // Change link should not be present
      expect(screen.queryByText('Change')).not.toBeInTheDocument();
    });
  });

  describe('Location Loading State', () => {
    it('should show loader while locations are loading', () => {
      vi.mocked(useLocations).mockReturnValue({
        data: undefined,
        isLoading: true,
        isSuccess: false,
        isError: false,
        error: null,
      } as any);

      vi.mocked(useServices).mockReturnValue({
        data: [createMockService()],
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
      } as any);

      renderBookingPage(mockUser, mockBusiness, queryClient);

      // Should start at service step while locations load
      expect(screen.getByText('Step 1: Select a Service')).toBeInTheDocument();
    });
  });
});
