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 platform API
vi.mock('../../api/platform', () => ({
  getBusinesses: vi.fn(),
  getUsers: vi.fn(),
  getBusinessUsers: vi.fn(),
  updateBusiness: vi.fn(),
  createBusiness: vi.fn(),
  deleteBusiness: vi.fn(),
  getTenantInvitations: vi.fn(),
  createTenantInvitation: vi.fn(),
  resendTenantInvitation: vi.fn(),
  cancelTenantInvitation: vi.fn(),
  getInvitationByToken: vi.fn(),
  acceptInvitation: vi.fn(),
}));

import {
  useBusinesses,
  usePlatformUsers,
  useBusinessUsers,
  useUpdateBusiness,
  useCreateBusiness,
  useDeleteBusiness,
  useTenantInvitations,
  useCreateTenantInvitation,
  useResendTenantInvitation,
  useCancelTenantInvitation,
  useInvitationByToken,
  useAcceptInvitation,
} from '../usePlatform';

import * as platformApi from '../../api/platform';

// 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('usePlatform hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  // ============================================================================
  // Query Hooks
  // ============================================================================

  describe('useBusinesses', () => {
    it('fetches all businesses successfully', async () => {
      const mockBusinesses = [
        {
          id: 1,
          name: 'Business 1',
          subdomain: 'biz1',
          tier: 'PROFESSIONAL',
          is_active: true,
          created_on: '2024-01-01',
          user_count: 5,
          owner: {
            id: 10,
            username: 'owner1',
            full_name: 'Owner One',
            email: 'owner1@test.com',
            role: 'OWNER',
            email_verified: true,
          },
          max_users: 10,
          max_resources: 20,
          can_manage_oauth_credentials: false,
          can_accept_payments: true,
          can_use_custom_domain: false,
          can_white_label: false,
          can_api_access: false,
        },
        {
          id: 2,
          name: 'Business 2',
          subdomain: 'biz2',
          tier: 'FREE',
          is_active: true,
          created_on: '2024-01-02',
          user_count: 2,
          owner: null,
          max_users: 5,
          max_resources: 10,
          can_manage_oauth_credentials: false,
          can_accept_payments: false,
          can_use_custom_domain: false,
          can_white_label: false,
          can_api_access: false,
        },
      ];

      vi.mocked(platformApi.getBusinesses).mockResolvedValue(mockBusinesses);

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

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

      expect(platformApi.getBusinesses).toHaveBeenCalledOnce();
      expect(result.current.data).toEqual(mockBusinesses);
      expect(result.current.data).toHaveLength(2);
    });

    it('handles fetch error', async () => {
      const mockError = new Error('Failed to fetch businesses');
      vi.mocked(platformApi.getBusinesses).mockRejectedValue(mockError);

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

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

      expect(result.current.error).toBe(mockError);
    });

    it('uses correct query key', async () => {
      vi.mocked(platformApi.getBusinesses).mockResolvedValue([]);

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

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

      // Query key should be ['platform', 'businesses']
      expect(platformApi.getBusinesses).toHaveBeenCalled();
    });
  });

  describe('usePlatformUsers', () => {
    it('fetches all platform users successfully', async () => {
      const mockUsers = [
        {
          id: 1,
          email: 'user1@test.com',
          username: 'user1',
          name: 'User One',
          role: 'OWNER',
          is_active: true,
          is_staff: false,
          is_superuser: false,
          email_verified: true,
          business: 1,
          business_name: 'Business 1',
          business_subdomain: 'biz1',
          date_joined: '2024-01-01',
          last_login: '2024-01-05',
        },
        {
          id: 2,
          email: 'user2@test.com',
          username: 'user2',
          role: 'STAFF',
          is_active: true,
          is_staff: false,
          is_superuser: false,
          email_verified: false,
          business: null,
          date_joined: '2024-01-02',
        },
      ];

      vi.mocked(platformApi.getUsers).mockResolvedValue(mockUsers);

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

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

      expect(platformApi.getUsers).toHaveBeenCalledOnce();
      expect(result.current.data).toEqual(mockUsers);
      expect(result.current.data).toHaveLength(2);
    });

    it('handles fetch error', async () => {
      const mockError = new Error('Unauthorized');
      vi.mocked(platformApi.getUsers).mockRejectedValue(mockError);

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

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

      expect(result.current.error).toBe(mockError);
    });
  });

  describe('useBusinessUsers', () => {
    it('fetches users for specific business when businessId is provided', async () => {
      const mockUsers = [
        {
          id: 1,
          email: 'owner@business.com',
          username: 'owner',
          role: 'OWNER',
          is_active: true,
          is_staff: false,
          is_superuser: false,
          email_verified: true,
          business: 5,
          business_name: 'Test Business',
          business_subdomain: 'test',
          date_joined: '2024-01-01',
        },
      ];

      vi.mocked(platformApi.getBusinessUsers).mockResolvedValue(mockUsers);

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

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

      expect(platformApi.getBusinessUsers).toHaveBeenCalledWith(5);
      expect(result.current.data).toEqual(mockUsers);
    });

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

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

      expect(platformApi.getBusinessUsers).not.toHaveBeenCalled();
      expect(result.current.data).toBeUndefined();
    });

    it('does not fetch when businessId is 0', async () => {
      const { result } = renderHook(() => useBusinessUsers(0), {
        wrapper: createWrapper(),
      });

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

      expect(platformApi.getBusinessUsers).not.toHaveBeenCalled();
    });

    it('handles fetch error', async () => {
      const mockError = new Error('Business not found');
      vi.mocked(platformApi.getBusinessUsers).mockRejectedValue(mockError);

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

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

      expect(result.current.error).toBe(mockError);
    });
  });

  // ============================================================================
  // Business Mutation Hooks
  // ============================================================================

  describe('useUpdateBusiness', () => {
    it('updates business and invalidates cache', async () => {
      const mockUpdatedBusiness = {
        id: 1,
        name: 'Updated Business',
        subdomain: 'updated',
        tier: 'ENTERPRISE',
        is_active: true,
        created_on: '2024-01-01',
        user_count: 10,
        owner: null,
        max_users: 50,
        max_resources: 100,
        can_manage_oauth_credentials: true,
        can_accept_payments: true,
        can_use_custom_domain: true,
        can_white_label: true,
        can_api_access: true,
      };

      vi.mocked(platformApi.updateBusiness).mockResolvedValue(mockUpdatedBusiness);

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

      await act(async () => {
        await result.current.mutateAsync({
          businessId: 1,
          data: {
            name: 'Updated Business',
            subscription_tier: 'ENTERPRISE',
            can_manage_oauth_credentials: true,
          },
        });
      });

      expect(platformApi.updateBusiness).toHaveBeenCalledWith(1, {
        name: 'Updated Business',
        subscription_tier: 'ENTERPRISE',
        can_manage_oauth_credentials: true,
      });
    });

    it('handles update error', async () => {
      const mockError = new Error('Validation failed');
      vi.mocked(platformApi.updateBusiness).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync({
            businessId: 1,
            data: { name: '' },
          });
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  describe('useCreateBusiness', () => {
    it('creates business and invalidates cache', async () => {
      const mockNewBusiness = {
        id: 3,
        name: 'New Business',
        subdomain: 'newbiz',
        tier: 'STARTER',
        is_active: true,
        created_on: '2024-01-10',
        user_count: 0,
        owner: null,
        max_users: 5,
        max_resources: 10,
        can_manage_oauth_credentials: false,
        can_accept_payments: false,
        can_use_custom_domain: false,
        can_white_label: false,
        can_api_access: false,
      };

      vi.mocked(platformApi.createBusiness).mockResolvedValue(mockNewBusiness);

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

      await act(async () => {
        await result.current.mutateAsync({
          name: 'New Business',
          subdomain: 'newbiz',
          subscription_tier: 'STARTER',
        });
      });

      expect(platformApi.createBusiness).toHaveBeenCalledWith({
        name: 'New Business',
        subdomain: 'newbiz',
        subscription_tier: 'STARTER',
      });
    });

    it('creates business with owner details', async () => {
      const mockNewBusiness = {
        id: 4,
        name: 'Business with Owner',
        subdomain: 'withowner',
        tier: 'PROFESSIONAL',
        is_active: true,
        created_on: '2024-01-10',
        user_count: 1,
        owner: {
          id: 20,
          username: 'newowner',
          full_name: 'New Owner',
          email: 'owner@new.com',
          role: 'OWNER',
          email_verified: false,
        },
        max_users: 10,
        max_resources: 20,
        can_manage_oauth_credentials: false,
        can_accept_payments: true,
        can_use_custom_domain: false,
        can_white_label: false,
        can_api_access: false,
      };

      vi.mocked(platformApi.createBusiness).mockResolvedValue(mockNewBusiness);

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

      await act(async () => {
        await result.current.mutateAsync({
          name: 'Business with Owner',
          subdomain: 'withowner',
          subscription_tier: 'PROFESSIONAL',
          owner_email: 'owner@new.com',
          owner_name: 'New Owner',
          owner_password: 'securepass123',
        });
      });

      expect(platformApi.createBusiness).toHaveBeenCalledWith({
        name: 'Business with Owner',
        subdomain: 'withowner',
        subscription_tier: 'PROFESSIONAL',
        owner_email: 'owner@new.com',
        owner_name: 'New Owner',
        owner_password: 'securepass123',
      });
    });

    it('handles creation error', async () => {
      const mockError = new Error('Subdomain already exists');
      vi.mocked(platformApi.createBusiness).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync({
            name: 'Duplicate',
            subdomain: 'duplicate',
          });
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  describe('useDeleteBusiness', () => {
    it('deletes business and invalidates cache', async () => {
      vi.mocked(platformApi.deleteBusiness).mockResolvedValue(undefined);

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

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

      expect(platformApi.deleteBusiness).toHaveBeenCalledWith(5);
    });

    it('handles deletion error', async () => {
      const mockError = new Error('Cannot delete business with active users');
      vi.mocked(platformApi.deleteBusiness).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync(1);
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  // ============================================================================
  // Tenant Invitation Query Hooks
  // ============================================================================

  describe('useTenantInvitations', () => {
    it('fetches all tenant invitations successfully', async () => {
      const mockInvitations = [
        {
          id: 1,
          email: 'invited1@test.com',
          token: 'token123',
          status: 'PENDING' as const,
          suggested_business_name: 'Suggested Biz 1',
          subscription_tier: 'PROFESSIONAL' as const,
          custom_max_users: null,
          custom_max_resources: null,
          permissions: {
            can_accept_payments: true,
          },
          personal_message: 'Welcome!',
          invited_by: 1,
          invited_by_email: 'admin@platform.com',
          created_at: '2024-01-01',
          expires_at: '2024-01-08',
          accepted_at: null,
          created_tenant: null,
          created_tenant_name: null,
          created_user: null,
          created_user_email: null,
        },
        {
          id: 2,
          email: 'accepted@test.com',
          token: 'token456',
          status: 'ACCEPTED' as const,
          suggested_business_name: 'Accepted Biz',
          subscription_tier: 'STARTER' as const,
          custom_max_users: 10,
          custom_max_resources: 20,
          permissions: {},
          personal_message: '',
          invited_by: 1,
          invited_by_email: 'admin@platform.com',
          created_at: '2024-01-01',
          expires_at: '2024-01-08',
          accepted_at: '2024-01-02',
          created_tenant: 5,
          created_tenant_name: 'Accepted Business',
          created_user: 10,
          created_user_email: 'accepted@test.com',
        },
      ];

      vi.mocked(platformApi.getTenantInvitations).mockResolvedValue(mockInvitations);

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

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

      expect(platformApi.getTenantInvitations).toHaveBeenCalledOnce();
      expect(result.current.data).toEqual(mockInvitations);
      expect(result.current.data).toHaveLength(2);
    });

    it('handles fetch error', async () => {
      const mockError = new Error('Failed to fetch invitations');
      vi.mocked(platformApi.getTenantInvitations).mockRejectedValue(mockError);

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

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

      expect(result.current.error).toBe(mockError);
    });
  });

  describe('useInvitationByToken', () => {
    it('fetches invitation details when token is provided', async () => {
      const mockInvitation = {
        email: 'invited@test.com',
        suggested_business_name: 'Test Business',
        subscription_tier: 'PROFESSIONAL',
        effective_max_users: 10,
        effective_max_resources: 20,
        permissions: {
          can_accept_payments: true,
          can_manage_oauth_credentials: false,
        },
        expires_at: '2024-12-31',
      };

      vi.mocked(platformApi.getInvitationByToken).mockResolvedValue(mockInvitation);

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

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

      expect(platformApi.getInvitationByToken).toHaveBeenCalledWith('valid-token-123');
      expect(result.current.data).toEqual(mockInvitation);
    });

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

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

      expect(platformApi.getInvitationByToken).not.toHaveBeenCalled();
      expect(result.current.data).toBeUndefined();
    });

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

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

      expect(platformApi.getInvitationByToken).not.toHaveBeenCalled();
    });

    it('does not retry on error (expired/invalid token)', async () => {
      const mockError = new Error('Invitation expired');
      vi.mocked(platformApi.getInvitationByToken).mockRejectedValue(mockError);

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

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

      // Should only be called once (no retries)
      expect(platformApi.getInvitationByToken).toHaveBeenCalledTimes(1);
      expect(result.current.error).toBe(mockError);
    });
  });

  // ============================================================================
  // Tenant Invitation Mutation Hooks
  // ============================================================================

  describe('useCreateTenantInvitation', () => {
    it('creates invitation and invalidates cache', async () => {
      const mockInvitation = {
        id: 10,
        email: 'new@invite.com',
        token: 'new-token',
        status: 'PENDING' as const,
        suggested_business_name: 'New Business',
        subscription_tier: 'STARTER' as const,
        custom_max_users: null,
        custom_max_resources: null,
        permissions: {},
        personal_message: 'Join us!',
        invited_by: 1,
        invited_by_email: 'admin@platform.com',
        created_at: '2024-01-10',
        expires_at: '2024-01-17',
        accepted_at: null,
        created_tenant: null,
        created_tenant_name: null,
        created_user: null,
        created_user_email: null,
      };

      vi.mocked(platformApi.createTenantInvitation).mockResolvedValue(mockInvitation);

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

      await act(async () => {
        await result.current.mutateAsync({
          email: 'new@invite.com',
          subscription_tier: 'STARTER',
          suggested_business_name: 'New Business',
          personal_message: 'Join us!',
        });
      });

      expect(platformApi.createTenantInvitation).toHaveBeenCalledWith({
        email: 'new@invite.com',
        subscription_tier: 'STARTER',
        suggested_business_name: 'New Business',
        personal_message: 'Join us!',
      });
    });

    it('creates invitation with custom limits and permissions', async () => {
      const mockInvitation = {
        id: 11,
        email: 'custom@invite.com',
        token: 'custom-token',
        status: 'PENDING' as const,
        suggested_business_name: 'Custom Biz',
        subscription_tier: 'ENTERPRISE' as const,
        custom_max_users: 100,
        custom_max_resources: 200,
        permissions: {
          can_manage_oauth_credentials: true,
          can_accept_payments: true,
          can_use_custom_domain: true,
        },
        personal_message: '',
        invited_by: 1,
        invited_by_email: 'admin@platform.com',
        created_at: '2024-01-10',
        expires_at: '2024-01-17',
        accepted_at: null,
        created_tenant: null,
        created_tenant_name: null,
        created_user: null,
        created_user_email: null,
      };

      vi.mocked(platformApi.createTenantInvitation).mockResolvedValue(mockInvitation);

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

      await act(async () => {
        await result.current.mutateAsync({
          email: 'custom@invite.com',
          subscription_tier: 'ENTERPRISE',
          custom_max_users: 100,
          custom_max_resources: 200,
          permissions: {
            can_manage_oauth_credentials: true,
            can_accept_payments: true,
            can_use_custom_domain: true,
          },
        });
      });

      expect(platformApi.createTenantInvitation).toHaveBeenCalledWith({
        email: 'custom@invite.com',
        subscription_tier: 'ENTERPRISE',
        custom_max_users: 100,
        custom_max_resources: 200,
        permissions: {
          can_manage_oauth_credentials: true,
          can_accept_payments: true,
          can_use_custom_domain: true,
        },
      });
    });

    it('handles creation error', async () => {
      const mockError = new Error('Email already invited');
      vi.mocked(platformApi.createTenantInvitation).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync({
            email: 'duplicate@test.com',
            subscription_tier: 'FREE',
          });
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  describe('useResendTenantInvitation', () => {
    it('resends invitation and invalidates cache', async () => {
      vi.mocked(platformApi.resendTenantInvitation).mockResolvedValue(undefined);

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

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

      expect(platformApi.resendTenantInvitation).toHaveBeenCalledWith(5);
    });

    it('handles resend error', async () => {
      const mockError = new Error('Invitation already accepted');
      vi.mocked(platformApi.resendTenantInvitation).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync(10);
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  describe('useCancelTenantInvitation', () => {
    it('cancels invitation and invalidates cache', async () => {
      vi.mocked(platformApi.cancelTenantInvitation).mockResolvedValue(undefined);

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

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

      expect(platformApi.cancelTenantInvitation).toHaveBeenCalledWith(3);
    });

    it('handles cancel error', async () => {
      const mockError = new Error('Invitation not found');
      vi.mocked(platformApi.cancelTenantInvitation).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync(999);
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  describe('useAcceptInvitation', () => {
    it('accepts invitation successfully', async () => {
      const mockResponse = {
        detail: 'Invitation accepted successfully',
      };

      vi.mocked(platformApi.acceptInvitation).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          token: 'valid-token',
          data: {
            email: 'user@test.com',
            password: 'securepass123',
            first_name: 'John',
            last_name: 'Doe',
            business_name: 'My Business',
            subdomain: 'mybiz',
            contact_email: 'contact@mybiz.com',
            phone: '+1234567890',
          },
        });
      });

      expect(platformApi.acceptInvitation).toHaveBeenCalledWith('valid-token', {
        email: 'user@test.com',
        password: 'securepass123',
        first_name: 'John',
        last_name: 'Doe',
        business_name: 'My Business',
        subdomain: 'mybiz',
        contact_email: 'contact@mybiz.com',
        phone: '+1234567890',
      });
    });

    it('accepts invitation with minimal data', async () => {
      const mockResponse = {
        detail: 'Invitation accepted',
      };

      vi.mocked(platformApi.acceptInvitation).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          token: 'minimal-token',
          data: {
            email: 'user@test.com',
            password: 'pass123',
            first_name: 'Jane',
            last_name: 'Smith',
            business_name: 'Jane Biz',
            subdomain: 'janebiz',
          },
        });
      });

      expect(platformApi.acceptInvitation).toHaveBeenCalledWith('minimal-token', {
        email: 'user@test.com',
        password: 'pass123',
        first_name: 'Jane',
        last_name: 'Smith',
        business_name: 'Jane Biz',
        subdomain: 'janebiz',
      });
    });

    it('handles acceptance error', async () => {
      const mockError = new Error('Subdomain already taken');
      vi.mocked(platformApi.acceptInvitation).mockRejectedValue(mockError);

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

      let thrownError;
      await act(async () => {
        try {
          await result.current.mutateAsync({
            token: 'token',
            data: {
              email: 'user@test.com',
              password: 'pass',
              first_name: 'Test',
              last_name: 'User',
              business_name: 'Test',
              subdomain: 'taken',
            },
          });
        } catch (error) {
          thrownError = error;
        }
      });

      expect(thrownError).toBe(mockError);
    });
  });

  // ============================================================================
  // Cache Invalidation Tests
  // ============================================================================

  describe('Cache invalidation', () => {
    it('useUpdateBusiness invalidates businesses query', async () => {
      vi.mocked(platformApi.updateBusiness).mockResolvedValue({
        id: 1,
        name: 'Updated',
        subdomain: 'updated',
        tier: 'FREE',
        is_active: true,
        created_on: '2024-01-01',
        user_count: 0,
        owner: null,
        max_users: 5,
        max_resources: 10,
        can_manage_oauth_credentials: false,
        can_accept_payments: false,
        can_use_custom_domain: false,
        can_white_label: false,
        can_api_access: false,
      });

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

      const spy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

      await act(async () => {
        await result.current.mutateAsync({
          businessId: 1,
          data: { name: 'Updated' },
        });
      });

      expect(spy).toHaveBeenCalledWith({ queryKey: ['platform', 'businesses'] });
    });

    it('useCreateBusiness invalidates businesses query', async () => {
      vi.mocked(platformApi.createBusiness).mockResolvedValue({
        id: 1,
        name: 'New',
        subdomain: 'new',
        tier: 'FREE',
        is_active: true,
        created_on: '2024-01-01',
        user_count: 0,
        owner: null,
        max_users: 5,
        max_resources: 10,
        can_manage_oauth_credentials: false,
        can_accept_payments: false,
        can_use_custom_domain: false,
        can_white_label: false,
        can_api_access: false,
      });

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

      const spy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

      await act(async () => {
        await result.current.mutateAsync({
          name: 'New',
          subdomain: 'new',
        });
      });

      expect(spy).toHaveBeenCalledWith({ queryKey: ['platform', 'businesses'] });
    });

    it('useDeleteBusiness invalidates businesses query', async () => {
      vi.mocked(platformApi.deleteBusiness).mockResolvedValue(undefined);

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

      const spy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

      expect(spy).toHaveBeenCalledWith({ queryKey: ['platform', 'businesses'] });
    });

    it('useCreateTenantInvitation invalidates invitations query', async () => {
      vi.mocked(platformApi.createTenantInvitation).mockResolvedValue({
        id: 1,
        email: 'test@test.com',
        token: 'token',
        status: 'PENDING',
        suggested_business_name: 'Test',
        subscription_tier: 'FREE',
        custom_max_users: null,
        custom_max_resources: null,
        permissions: {},
        personal_message: '',
        invited_by: 1,
        invited_by_email: 'admin@test.com',
        created_at: '2024-01-01',
        expires_at: '2024-01-08',
        accepted_at: null,
        created_tenant: null,
        created_tenant_name: null,
        created_user: null,
        created_user_email: null,
      });

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

      const spy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

      expect(spy).toHaveBeenCalledWith({ queryKey: ['platform', 'tenant-invitations'] });
    });

    it('useResendTenantInvitation invalidates invitations query', async () => {
      vi.mocked(platformApi.resendTenantInvitation).mockResolvedValue(undefined);

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

      const spy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

      expect(spy).toHaveBeenCalledWith({ queryKey: ['platform', 'tenant-invitations'] });
    });

    it('useCancelTenantInvitation invalidates invitations query', async () => {
      vi.mocked(platformApi.cancelTenantInvitation).mockResolvedValue(undefined);

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

      const spy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

      expect(spy).toHaveBeenCalledWith({ queryKey: ['platform', 'tenant-invitations'] });
    });
  });
});
