import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';

// Mock apiClient
vi.mock('../../api/client', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
    delete: vi.fn(),
  },
}));

import {
  useInvitations,
  useCreateInvitation,
  useCancelInvitation,
  useResendInvitation,
  useInvitationDetails,
  useAcceptInvitation,
  useDeclineInvitation,
  StaffInvitation,
  InvitationDetails,
  CreateInvitationData,
} from '../useInvitations';
import apiClient from '../../api/client';

// Create wrapper
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return function Wrapper({ children }: { children: React.ReactNode }) {
    return React.createElement(QueryClientProvider, { client: queryClient }, children);
  };
};

describe('useInvitations hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('useInvitations', () => {
    it('fetches pending invitations successfully', async () => {
      const mockInvitations: StaffInvitation[] = [
        {
          id: 1,
          email: 'john@example.com',
          role: 'TENANT_MANAGER',
          role_display: 'Manager',
          status: 'PENDING',
          invited_by: 5,
          invited_by_name: 'Admin User',
          created_at: '2024-01-01T10:00:00Z',
          expires_at: '2024-01-08T10:00:00Z',
          accepted_at: null,
          create_bookable_resource: false,
          resource_name: '',
          permissions: { can_invite_staff: true },
        },
        {
          id: 2,
          email: 'jane@example.com',
          role: 'TENANT_STAFF',
          role_display: 'Staff',
          status: 'PENDING',
          invited_by: 5,
          invited_by_name: 'Admin User',
          created_at: '2024-01-02T10:00:00Z',
          expires_at: '2024-01-09T10:00:00Z',
          accepted_at: null,
          create_bookable_resource: true,
          resource_name: 'Jane',
          permissions: {},
        },
      ];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockInvitations });

      const { result } = renderHook(() => useInvitations(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(apiClient.get).toHaveBeenCalledWith('/staff/invitations/');
      expect(result.current.data).toEqual(mockInvitations);
      expect(result.current.data).toHaveLength(2);
    });

    it('returns empty array when no invitations exist', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const { result } = renderHook(() => useInvitations(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data).toEqual([]);
    });

    it('handles API errors gracefully', async () => {
      vi.mocked(apiClient.get).mockRejectedValue(new Error('Network error'));

      const { result } = renderHook(() => useInvitations(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      expect(result.current.data).toBeUndefined();
    });

    it('uses correct query key for cache management', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
        },
      });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      renderHook(() => useInvitations(), { wrapper });

      await waitFor(() => {
        const cache = queryClient.getQueryCache();
        const queries = cache.findAll({ queryKey: ['invitations'] });
        expect(queries.length).toBe(1);
      });
    });
  });

  describe('useCreateInvitation', () => {
    it('creates invitation with minimal data', async () => {
      const invitationData: CreateInvitationData = {
        email: 'new@example.com',
        role: 'TENANT_STAFF',
      };

      const mockResponse = {
        id: 3,
        email: 'new@example.com',
        role: 'TENANT_STAFF',
        status: 'PENDING',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useCreateInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(invitationData);
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/', invitationData);
    });

    it('creates invitation with full data including resource', async () => {
      const invitationData: CreateInvitationData = {
        email: 'staff@example.com',
        role: 'TENANT_STAFF',
        create_bookable_resource: true,
        resource_name: 'New Staff Member',
        permissions: {
          can_view_all_schedules: true,
          can_manage_own_appointments: true,
        },
      };

      const mockResponse = {
        id: 4,
        email: 'staff@example.com',
        role: 'TENANT_STAFF',
        create_bookable_resource: true,
        resource_name: 'New Staff Member',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useCreateInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(invitationData);
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/', invitationData);
    });

    it('creates manager invitation with permissions', async () => {
      const invitationData: CreateInvitationData = {
        email: 'manager@example.com',
        role: 'TENANT_MANAGER',
        permissions: {
          can_invite_staff: true,
          can_manage_resources: true,
          can_manage_services: true,
          can_view_reports: true,
          can_access_settings: false,
          can_refund_payments: false,
        },
      };

      const mockResponse = { id: 5, email: 'manager@example.com', role: 'TENANT_MANAGER' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useCreateInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(invitationData);
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/', invitationData);
    });

    it('invalidates invitations query on success', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1 } });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useCreateInvitation(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync({
          email: 'test@example.com',
          role: 'TENANT_STAFF',
        });
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['invitations'] });
    });

    it('handles API errors during creation', async () => {
      const errorMessage = 'Email already invited';
      vi.mocked(apiClient.post).mockRejectedValue(new Error(errorMessage));

      const { result } = renderHook(() => useCreateInvitation(), {
        wrapper: createWrapper(),
      });

      let caughtError: Error | null = null;
      await act(async () => {
        try {
          await result.current.mutateAsync({
            email: 'duplicate@example.com',
            role: 'TENANT_STAFF',
          });
        } catch (error) {
          caughtError = error as Error;
        }
      });

      expect(caughtError).toBeInstanceOf(Error);
      expect(caughtError?.message).toBe(errorMessage);
    });

    it('returns created invitation data', async () => {
      const mockResponse = {
        id: 10,
        email: 'created@example.com',
        role: 'TENANT_STAFF',
        status: 'PENDING',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useCreateInvitation(), {
        wrapper: createWrapper(),
      });

      let responseData;
      await act(async () => {
        responseData = await result.current.mutateAsync({
          email: 'created@example.com',
          role: 'TENANT_STAFF',
        });
      });

      expect(responseData).toEqual(mockResponse);
    });
  });

  describe('useCancelInvitation', () => {
    it('cancels invitation by id', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({ data: undefined });

      const { result } = renderHook(() => useCancelInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(1);
      });

      expect(apiClient.delete).toHaveBeenCalledWith('/staff/invitations/1/');
    });

    it('invalidates invitations query on success', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      vi.mocked(apiClient.delete).mockResolvedValue({ data: undefined });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useCancelInvitation(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync(5);
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['invitations'] });
    });

    it('handles API errors during cancellation', async () => {
      const errorMessage = 'Invitation not found';
      vi.mocked(apiClient.delete).mockRejectedValue(new Error(errorMessage));

      const { result } = renderHook(() => useCancelInvitation(), {
        wrapper: createWrapper(),
      });

      let caughtError: Error | null = null;
      await act(async () => {
        try {
          await result.current.mutateAsync(999);
        } catch (error) {
          caughtError = error as Error;
        }
      });

      expect(caughtError).toBeInstanceOf(Error);
      expect(caughtError?.message).toBe(errorMessage);
    });
  });

  describe('useResendInvitation', () => {
    it('resends invitation email', async () => {
      const mockResponse = { message: 'Invitation email sent' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useResendInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(2);
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/2/resend/');
    });

    it('returns response data', async () => {
      const mockResponse = { message: 'Email resent successfully', sent_at: '2024-01-01T12:00:00Z' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useResendInvitation(), {
        wrapper: createWrapper(),
      });

      let responseData;
      await act(async () => {
        responseData = await result.current.mutateAsync(3);
      });

      expect(responseData).toEqual(mockResponse);
    });

    it('handles API errors during resend', async () => {
      const errorMessage = 'Invitation already accepted';
      vi.mocked(apiClient.post).mockRejectedValue(new Error(errorMessage));

      const { result } = renderHook(() => useResendInvitation(), {
        wrapper: createWrapper(),
      });

      let caughtError: Error | null = null;
      await act(async () => {
        try {
          await result.current.mutateAsync(10);
        } catch (error) {
          caughtError = error as Error;
        }
      });

      expect(caughtError).toBeInstanceOf(Error);
      expect(caughtError?.message).toBe(errorMessage);
    });

    it('does not invalidate queries (resend does not modify invitation list)', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      vi.mocked(apiClient.post).mockResolvedValue({ data: {} });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useResendInvitation(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync(1);
      });

      // Should not invalidate invitations query
      expect(invalidateSpy).not.toHaveBeenCalled();
    });
  });

  describe('useInvitationDetails', () => {
    it('fetches platform tenant invitation first and returns with tenant type', async () => {
      const mockPlatformInvitation: Omit<InvitationDetails, 'invitation_type'> = {
        email: 'tenant@example.com',
        role: 'OWNER',
        role_display: 'Business Owner',
        business_name: 'New Business',
        invited_by: 'Platform Admin',
        expires_at: '2024-01-15T10:00:00Z',
      };

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockPlatformInvitation });

      const { result } = renderHook(() => useInvitationDetails('valid-token-123'), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(apiClient.get).toHaveBeenCalledWith('/platform/tenant-invitations/token/valid-token-123/');
      expect(result.current.data).toEqual({
        ...mockPlatformInvitation,
        invitation_type: 'tenant',
      });
    });

    it('falls back to staff invitation when platform request fails', async () => {
      const mockStaffInvitation: Omit<InvitationDetails, 'invitation_type'> = {
        email: 'staff@example.com',
        role: 'TENANT_STAFF',
        role_display: 'Staff',
        business_name: 'Existing Business',
        invited_by: 'Manager',
        expires_at: '2024-01-15T10:00:00Z',
        create_bookable_resource: true,
        resource_name: 'Staff Member',
      };

      // First call fails (platform), second succeeds (staff)
      vi.mocked(apiClient.get)
        .mockRejectedValueOnce(new Error('Not found'))
        .mockResolvedValueOnce({ data: mockStaffInvitation });

      const { result } = renderHook(() => useInvitationDetails('staff-token-456'), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(apiClient.get).toHaveBeenCalledWith('/platform/tenant-invitations/token/staff-token-456/');
      expect(apiClient.get).toHaveBeenCalledWith('/staff/invitations/token/staff-token-456/');
      expect(result.current.data).toEqual({
        ...mockStaffInvitation,
        invitation_type: 'staff',
      });
    });

    it('returns error when both platform and staff requests fail', async () => {
      vi.mocked(apiClient.get)
        .mockRejectedValueOnce(new Error('Platform not found'))
        .mockRejectedValueOnce(new Error('Staff not found'));

      const { result } = renderHook(() => useInvitationDetails('invalid-token'), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      expect(result.current.data).toBeUndefined();
    });

    it('does not fetch when token is null', async () => {
      const { result } = renderHook(() => useInvitationDetails(null), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false);
      });

      expect(apiClient.get).not.toHaveBeenCalled();
      expect(result.current.data).toBeUndefined();
    });

    it('does not fetch when token is empty string', async () => {
      const { result } = renderHook(() => useInvitationDetails(''), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false);
      });

      expect(apiClient.get).not.toHaveBeenCalled();
    });

    it('does not retry on failure', async () => {
      vi.mocked(apiClient.get)
        .mockRejectedValueOnce(new Error('Platform error'))
        .mockRejectedValueOnce(new Error('Staff error'));

      const { result } = renderHook(() => useInvitationDetails('token'), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      // Called twice total: once for platform, once for staff (no retries)
      expect(apiClient.get).toHaveBeenCalledTimes(2);
    });
  });

  describe('useAcceptInvitation', () => {
    const acceptPayload = {
      token: 'test-token',
      firstName: 'John',
      lastName: 'Doe',
      password: 'SecurePass123!',
    };

    it('accepts staff invitation when invitationType is staff', async () => {
      const mockResponse = { message: 'Invitation accepted', user_id: 1 };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useAcceptInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync({
          ...acceptPayload,
          invitationType: 'staff',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/token/test-token/accept/', {
        first_name: 'John',
        last_name: 'Doe',
        password: 'SecurePass123!',
      });
      expect(apiClient.post).toHaveBeenCalledTimes(1);
    });

    it('tries platform tenant invitation first when invitationType not provided', async () => {
      const mockResponse = { message: 'Tenant invitation accepted', business_id: 5 };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useAcceptInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(acceptPayload);
      });

      expect(apiClient.post).toHaveBeenCalledWith('/platform/tenant-invitations/token/test-token/accept/', {
        first_name: 'John',
        last_name: 'Doe',
        password: 'SecurePass123!',
      });
    });

    it('tries platform tenant invitation first when invitationType is tenant', async () => {
      const mockResponse = { message: 'Tenant invitation accepted' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useAcceptInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync({
          ...acceptPayload,
          invitationType: 'tenant',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/platform/tenant-invitations/token/test-token/accept/', {
        first_name: 'John',
        last_name: 'Doe',
        password: 'SecurePass123!',
      });
    });

    it('falls back to staff invitation when platform request fails', async () => {
      const mockResponse = { message: 'Staff invitation accepted' };
      vi.mocked(apiClient.post)
        .mockRejectedValueOnce(new Error('Platform not found'))
        .mockResolvedValueOnce({ data: mockResponse });

      const { result } = renderHook(() => useAcceptInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync(acceptPayload);
      });

      expect(apiClient.post).toHaveBeenCalledWith('/platform/tenant-invitations/token/test-token/accept/', {
        first_name: 'John',
        last_name: 'Doe',
        password: 'SecurePass123!',
      });
      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/token/test-token/accept/', {
        first_name: 'John',
        last_name: 'Doe',
        password: 'SecurePass123!',
      });
      expect(apiClient.post).toHaveBeenCalledTimes(2);
    });

    it('throws error when both platform and staff requests fail', async () => {
      vi.mocked(apiClient.post)
        .mockRejectedValueOnce(new Error('Platform error'))
        .mockRejectedValueOnce(new Error('Staff error'));

      const { result } = renderHook(() => useAcceptInvitation(), {
        wrapper: createWrapper(),
      });

      let caughtError: Error | null = null;
      await act(async () => {
        try {
          await result.current.mutateAsync(acceptPayload);
        } catch (error) {
          caughtError = error as Error;
        }
      });

      expect(caughtError).toBeInstanceOf(Error);
      expect(caughtError?.message).toBe('Staff error');
    });

    it('returns response data on successful acceptance', async () => {
      const mockResponse = {
        message: 'Success',
        user: { id: 1, email: 'john@example.com' },
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useAcceptInvitation(), {
        wrapper: createWrapper(),
      });

      let responseData;
      await act(async () => {
        responseData = await result.current.mutateAsync({
          ...acceptPayload,
          invitationType: 'staff',
        });
      });

      expect(responseData).toEqual(mockResponse);
    });
  });

  describe('useDeclineInvitation', () => {
    it('declines staff invitation', async () => {
      const mockResponse = { message: 'Invitation declined' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useDeclineInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync({
          token: 'staff-token',
          invitationType: 'staff',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/token/staff-token/decline/');
    });

    it('attempts to decline tenant invitation', async () => {
      const mockResponse = { message: 'Tenant invitation declined' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useDeclineInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync({
          token: 'tenant-token',
          invitationType: 'tenant',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/platform/tenant-invitations/token/tenant-token/decline/');
    });

    it('returns success status when tenant decline endpoint does not exist', async () => {
      vi.mocked(apiClient.post).mockRejectedValue(new Error('Not found'));

      const { result } = renderHook(() => useDeclineInvitation(), {
        wrapper: createWrapper(),
      });

      let responseData;
      await act(async () => {
        responseData = await result.current.mutateAsync({
          token: 'tenant-token',
          invitationType: 'tenant',
        });
      });

      expect(responseData).toEqual({ status: 'declined' });
    });

    it('declines staff invitation when invitationType not provided', async () => {
      const mockResponse = { message: 'Staff invitation declined' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useDeclineInvitation(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync({
          token: 'default-token',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/staff/invitations/token/default-token/decline/');
    });

    it('handles API errors for staff invitation decline', async () => {
      const errorMessage = 'Invitation already processed';
      vi.mocked(apiClient.post).mockRejectedValue(new Error(errorMessage));

      const { result } = renderHook(() => useDeclineInvitation(), {
        wrapper: createWrapper(),
      });

      let caughtError: Error | null = null;
      await act(async () => {
        try {
          await result.current.mutateAsync({
            token: 'invalid-token',
            invitationType: 'staff',
          });
        } catch (error) {
          caughtError = error as Error;
        }
      });

      expect(caughtError).toBeInstanceOf(Error);
      expect(caughtError?.message).toBe(errorMessage);
    });

    it('returns response data on successful decline', async () => {
      const mockResponse = {
        message: 'Successfully declined',
        invitation_id: 5,
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const { result } = renderHook(() => useDeclineInvitation(), {
        wrapper: createWrapper(),
      });

      let responseData;
      await act(async () => {
        responseData = await result.current.mutateAsync({
          token: 'token',
          invitationType: 'staff',
        });
      });

      expect(responseData).toEqual(mockResponse);
    });
  });

  describe('Edge cases and integration scenarios', () => {
    it('handles multiple invitation operations in sequence', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      // Mock responses
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1 } });
      vi.mocked(apiClient.delete).mockResolvedValue({ data: undefined });

      const { result: createResult } = renderHook(() => useCreateInvitation(), { wrapper });
      const { result: listResult } = renderHook(() => useInvitations(), { wrapper });
      const { result: cancelResult } = renderHook(() => useCancelInvitation(), { wrapper });

      // Create invitation
      await act(async () => {
        await createResult.current.mutateAsync({
          email: 'test@example.com',
          role: 'TENANT_STAFF',
        });
      });

      // Cancel invitation
      await act(async () => {
        await cancelResult.current.mutateAsync(1);
      });

      // Verify list is called
      await waitFor(() => {
        expect(listResult.current.isSuccess).toBe(true);
      });

      expect(apiClient.post).toHaveBeenCalled();
      expect(apiClient.delete).toHaveBeenCalled();
    });

    it('handles concurrent invitation details fetching with different tokens', async () => {
      const platformData = { email: 'platform@example.com', business_name: 'Platform Biz' };
      const staffData = { email: 'staff@example.com', business_name: 'Staff Biz' };

      vi.mocked(apiClient.get)
        .mockResolvedValueOnce({ data: platformData })
        .mockRejectedValueOnce(new Error('Not found'))
        .mockResolvedValueOnce({ data: staffData });

      const { result: result1 } = renderHook(() => useInvitationDetails('token1'), {
        wrapper: createWrapper(),
      });

      const { result: result2 } = renderHook(() => useInvitationDetails('token2'), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result1.current.isSuccess).toBe(true);
        expect(result2.current.isSuccess).toBe(true);
      });

      expect(result1.current.data?.invitation_type).toBe('tenant');
      expect(result2.current.data?.invitation_type).toBe('staff');
    });
  });
});
