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 the domains API
vi.mock('../../api/domains', () => ({
  searchDomains: vi.fn(),
  getDomainPrices: vi.fn(),
  registerDomain: vi.fn(),
  getRegisteredDomains: vi.fn(),
  getDomainRegistration: vi.fn(),
  updateNameservers: vi.fn(),
  toggleAutoRenew: vi.fn(),
  renewDomain: vi.fn(),
  syncDomain: vi.fn(),
  getSearchHistory: vi.fn(),
}));

import {
  useDomainSearch,
  useDomainPrices,
  useRegisterDomain,
  useRegisteredDomains,
  useDomainRegistration,
  useUpdateNameservers,
  useToggleAutoRenew,
  useRenewDomain,
  useSyncDomain,
  useSearchHistory,
} from '../useDomains';
import * as domainsApi from '../../api/domains';

// Create wrapper with fresh QueryClient for each test
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);
  };
};

// Mock data
const mockDomainAvailability = [
  {
    domain: 'example.com',
    available: true,
    price: 12.99,
    premium: false,
    premium_price: null,
  },
  {
    domain: 'example.net',
    available: false,
    price: null,
    premium: false,
    premium_price: null,
  },
];

const mockDomainPrices = [
  { tld: '.com', registration: 12.99, renewal: 12.99, transfer: 12.99 },
  { tld: '.net', registration: 14.99, renewal: 14.99, transfer: 14.99 },
  { tld: '.org', registration: 13.99, renewal: 13.99, transfer: 13.99 },
];

const mockDomainRegistration = {
  id: 1,
  domain: 'example.com',
  status: 'active' as const,
  registered_at: '2024-01-01T00:00:00Z',
  expires_at: '2025-01-01T00:00:00Z',
  auto_renew: true,
  whois_privacy: true,
  purchase_price: 12.99,
  renewal_price: 12.99,
  nameservers: ['ns1.smoothschedule.com', 'ns2.smoothschedule.com'],
  days_until_expiry: 365,
  is_expiring_soon: false,
  created_at: '2024-01-01T00:00:00Z',
  registrant_first_name: 'John',
  registrant_last_name: 'Doe',
  registrant_email: 'john@example.com',
};

const mockRegisteredDomains = [
  mockDomainRegistration,
  {
    ...mockDomainRegistration,
    id: 2,
    domain: 'another.com',
    auto_renew: false,
  },
];

const mockSearchHistory = [
  {
    id: 1,
    searched_domain: 'example.com',
    was_available: true,
    price: 12.99,
    searched_at: '2024-01-01T00:00:00Z',
  },
  {
    id: 2,
    searched_domain: 'taken.com',
    was_available: false,
    price: null,
    searched_at: '2024-01-02T00:00:00Z',
  },
];

const mockRegisterRequest = {
  domain: 'example.com',
  years: 1,
  whois_privacy: true,
  auto_renew: true,
  nameservers: ['ns1.smoothschedule.com', 'ns2.smoothschedule.com'],
  contact: {
    first_name: 'John',
    last_name: 'Doe',
    email: 'john@example.com',
    phone: '+1234567890',
    address: '123 Main St',
    city: 'New York',
    state: 'NY',
    zip_code: '10001',
    country: 'US',
  },
  auto_configure: true,
};

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

  // ============================================
  // Search & Pricing
  // ============================================

  describe('useDomainSearch', () => {
    it('searches for domain availability', async () => {
      vi.mocked(domainsApi.searchDomains).mockResolvedValue(mockDomainAvailability);

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

      let searchData;
      await act(async () => {
        searchData = await result.current.mutateAsync({
          query: 'example',
          tlds: ['.com', '.net'],
        });
      });

      expect(domainsApi.searchDomains).toHaveBeenCalledWith('example', ['.com', '.net']);
      expect(searchData).toEqual(mockDomainAvailability);
    });

    it('uses default TLDs when not provided', async () => {
      vi.mocked(domainsApi.searchDomains).mockResolvedValue(mockDomainAvailability);

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

      await act(async () => {
        await result.current.mutateAsync({ query: 'example' });
      });

      expect(domainsApi.searchDomains).toHaveBeenCalledWith('example', undefined);
    });

    it('invalidates search history on successful search', async () => {
      vi.mocked(domainsApi.searchDomains).mockResolvedValue(mockDomainAvailability);
      vi.mocked(domainsApi.getSearchHistory).mockResolvedValue(mockSearchHistory);

      const wrapper = createWrapper();

      // First render the search history hook
      const { result: historyResult } = renderHook(() => useSearchHistory(), {
        wrapper,
      });

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

      // Now perform a search which should invalidate history
      const { result: searchResult } = renderHook(() => useDomainSearch(), {
        wrapper,
      });

      await act(async () => {
        await searchResult.current.mutateAsync({ query: 'example' });
      });

      // Verify search history was called again (invalidated and refetched)
      await waitFor(() => {
        expect(domainsApi.getSearchHistory).toHaveBeenCalledTimes(2);
      });
    });

    it('handles search errors', async () => {
      vi.mocked(domainsApi.searchDomains).mockRejectedValue(new Error('Search failed'));

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

      let caughtError;
      await act(async () => {
        try {
          await result.current.mutateAsync({ query: 'example' });
        } catch (error) {
          caughtError = error;
        }
      });

      expect(caughtError).toEqual(new Error('Search failed'));
    });
  });

  describe('useDomainPrices', () => {
    it('fetches domain prices', async () => {
      vi.mocked(domainsApi.getDomainPrices).mockResolvedValue(mockDomainPrices);

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

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

      expect(domainsApi.getDomainPrices).toHaveBeenCalledTimes(1);
      expect(result.current.data).toEqual(mockDomainPrices);
    });

    it('uses staleTime of 5 minutes', async () => {
      vi.mocked(domainsApi.getDomainPrices).mockResolvedValue(mockDomainPrices);

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

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

      // Verify staleTime is configured
      expect(result.current.dataUpdatedAt).toBeGreaterThan(0);
    });

    it('handles price fetch errors', async () => {
      vi.mocked(domainsApi.getDomainPrices).mockRejectedValue(new Error('Price fetch failed'));

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

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

      expect(result.current.error).toEqual(new Error('Price fetch failed'));
    });
  });

  // ============================================
  // Registration
  // ============================================

  describe('useRegisterDomain', () => {
    it('registers a new domain', async () => {
      vi.mocked(domainsApi.registerDomain).mockResolvedValue(mockDomainRegistration);

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

      let registrationData;
      await act(async () => {
        registrationData = await result.current.mutateAsync(mockRegisterRequest);
      });

      expect(domainsApi.registerDomain).toHaveBeenCalledWith(mockRegisterRequest);
      expect(registrationData).toEqual(mockDomainRegistration);
    });

    it('invalidates registrations and customDomains on success', async () => {
      vi.mocked(domainsApi.registerDomain).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      // First render registered domains
      const { result: domainsResult } = renderHook(() => useRegisteredDomains(), {
        wrapper,
      });

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

      // Register a new domain
      const { result: registerResult } = renderHook(() => useRegisterDomain(), {
        wrapper,
      });

      await act(async () => {
        await registerResult.current.mutateAsync(mockRegisterRequest);
      });

      // Verify registrations were invalidated and refetched
      await waitFor(() => {
        expect(domainsApi.getRegisteredDomains).toHaveBeenCalledTimes(2);
      });
    });

    it('handles registration errors', async () => {
      vi.mocked(domainsApi.registerDomain).mockRejectedValue(
        new Error('Registration failed')
      );

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

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

      expect(caughtError).toEqual(new Error('Registration failed'));
    });
  });

  describe('useRegisteredDomains', () => {
    it('fetches all registered domains', async () => {
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

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

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

      expect(domainsApi.getRegisteredDomains).toHaveBeenCalledTimes(1);
      expect(result.current.data).toEqual(mockRegisteredDomains);
      expect(result.current.data).toHaveLength(2);
    });

    it('uses staleTime of 30 seconds', async () => {
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

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

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

      expect(result.current.dataUpdatedAt).toBeGreaterThan(0);
    });

    it('handles fetch errors', async () => {
      vi.mocked(domainsApi.getRegisteredDomains).mockRejectedValue(
        new Error('Fetch failed')
      );

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

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

      expect(result.current.error).toEqual(new Error('Fetch failed'));
    });
  });

  describe('useDomainRegistration', () => {
    it('fetches single domain registration by id', async () => {
      vi.mocked(domainsApi.getDomainRegistration).mockResolvedValue(mockDomainRegistration);

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

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

      expect(domainsApi.getDomainRegistration).toHaveBeenCalledWith(1);
      expect(result.current.data).toEqual(mockDomainRegistration);
    });

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

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

      expect(domainsApi.getDomainRegistration).not.toHaveBeenCalled();
      expect(result.current.data).toBeUndefined();
    });

    it('does not fetch when id is null/undefined', async () => {
      const { result } = renderHook(() => useDomainRegistration(null as any), {
        wrapper: createWrapper(),
      });

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

      expect(domainsApi.getDomainRegistration).not.toHaveBeenCalled();
    });

    it('handles fetch errors', async () => {
      vi.mocked(domainsApi.getDomainRegistration).mockRejectedValue(
        new Error('Domain not found')
      );

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

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

      expect(result.current.error).toEqual(new Error('Domain not found'));
    });
  });

  // ============================================
  // Domain Management
  // ============================================

  describe('useUpdateNameservers', () => {
    it('updates nameservers for a domain', async () => {
      const updatedDomain = {
        ...mockDomainRegistration,
        nameservers: ['ns1.custom.com', 'ns2.custom.com'],
      };
      vi.mocked(domainsApi.updateNameservers).mockResolvedValue(updatedDomain);

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

      let updateData;
      await act(async () => {
        updateData = await result.current.mutateAsync({
          id: 1,
          nameservers: ['ns1.custom.com', 'ns2.custom.com'],
        });
      });

      expect(domainsApi.updateNameservers).toHaveBeenCalledWith(1, [
        'ns1.custom.com',
        'ns2.custom.com',
      ]);
      expect(updateData).toEqual(updatedDomain);
    });

    it('updates cache optimistically with setQueryData', async () => {
      const updatedDomain = {
        ...mockDomainRegistration,
        nameservers: ['ns1.new.com', 'ns2.new.com'],
      };
      vi.mocked(domainsApi.getDomainRegistration).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.updateNameservers).mockResolvedValue(updatedDomain);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      // First fetch the domain
      const { result: domainResult } = renderHook(() => useDomainRegistration(1), {
        wrapper,
      });

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

      expect(domainResult.current.data?.nameservers).toEqual([
        'ns1.smoothschedule.com',
        'ns2.smoothschedule.com',
      ]);

      // Now update nameservers
      const { result: updateResult } = renderHook(() => useUpdateNameservers(), {
        wrapper,
      });

      let updateData;
      await act(async () => {
        updateData = await updateResult.current.mutateAsync({
          id: 1,
          nameservers: ['ns1.new.com', 'ns2.new.com'],
        });
      });

      // Verify the mutation returned updated data
      expect(updateData).toEqual(updatedDomain);

      // Refetch to get updated cache
      await act(async () => {
        await domainResult.current.refetch();
      });
    });

    it('invalidates registrations list', async () => {
      vi.mocked(domainsApi.updateNameservers).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      // Fetch registrations first
      const { result: domainsResult } = renderHook(() => useRegisteredDomains(), {
        wrapper,
      });

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

      // Update nameservers
      const { result: updateResult } = renderHook(() => useUpdateNameservers(), {
        wrapper,
      });

      await act(async () => {
        await updateResult.current.mutateAsync({
          id: 1,
          nameservers: ['ns1.new.com'],
        });
      });

      // Registrations should be refetched
      await waitFor(() => {
        expect(domainsApi.getRegisteredDomains).toHaveBeenCalledTimes(2);
      });
    });

    it('handles update errors', async () => {
      vi.mocked(domainsApi.updateNameservers).mockRejectedValue(
        new Error('Update failed')
      );

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

      let caughtError;
      await act(async () => {
        try {
          await result.current.mutateAsync({ id: 1, nameservers: ['ns1.new.com'] });
        } catch (error) {
          caughtError = error;
        }
      });

      expect(caughtError).toEqual(new Error('Update failed'));
    });
  });

  describe('useToggleAutoRenew', () => {
    it('toggles auto-renewal on', async () => {
      const updatedDomain = { ...mockDomainRegistration, auto_renew: true };
      vi.mocked(domainsApi.toggleAutoRenew).mockResolvedValue(updatedDomain);

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

      let toggleData;
      await act(async () => {
        toggleData = await result.current.mutateAsync({ id: 1, autoRenew: true });
      });

      expect(domainsApi.toggleAutoRenew).toHaveBeenCalledWith(1, true);
      expect(toggleData?.auto_renew).toBe(true);
    });

    it('toggles auto-renewal off', async () => {
      const updatedDomain = { ...mockDomainRegistration, auto_renew: false };
      vi.mocked(domainsApi.toggleAutoRenew).mockResolvedValue(updatedDomain);

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

      let toggleData;
      await act(async () => {
        toggleData = await result.current.mutateAsync({ id: 1, autoRenew: false });
      });

      expect(domainsApi.toggleAutoRenew).toHaveBeenCalledWith(1, false);
      expect(toggleData?.auto_renew).toBe(false);
    });

    it('updates cache with setQueryData', async () => {
      const updatedDomain = { ...mockDomainRegistration, auto_renew: false };
      vi.mocked(domainsApi.getDomainRegistration).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.toggleAutoRenew).mockResolvedValue(updatedDomain);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      // Fetch domain first
      const { result: domainResult } = renderHook(() => useDomainRegistration(1), {
        wrapper,
      });

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

      expect(domainResult.current.data?.auto_renew).toBe(true);

      // Toggle auto-renew
      const { result: toggleResult } = renderHook(() => useToggleAutoRenew(), {
        wrapper,
      });

      let toggleData;
      await act(async () => {
        toggleData = await toggleResult.current.mutateAsync({ id: 1, autoRenew: false });
      });

      // Verify mutation returned updated data
      expect(toggleData?.auto_renew).toBe(false);

      // Refetch to verify cache updated
      await act(async () => {
        await domainResult.current.refetch();
      });
    });

    it('handles toggle errors', async () => {
      vi.mocked(domainsApi.toggleAutoRenew).mockRejectedValue(
        new Error('Toggle failed')
      );

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

      let caughtError;
      await act(async () => {
        try {
          await result.current.mutateAsync({ id: 1, autoRenew: false });
        } catch (error) {
          caughtError = error;
        }
      });

      expect(caughtError).toEqual(new Error('Toggle failed'));
    });
  });

  describe('useRenewDomain', () => {
    it('renews domain for 1 year by default', async () => {
      const renewedDomain = {
        ...mockDomainRegistration,
        expires_at: '2026-01-01T00:00:00Z',
        days_until_expiry: 730,
      };
      vi.mocked(domainsApi.renewDomain).mockResolvedValue(renewedDomain);

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

      let renewData;
      await act(async () => {
        renewData = await result.current.mutateAsync({ id: 1 });
      });

      expect(domainsApi.renewDomain).toHaveBeenCalledWith(1, undefined);
      expect(renewData).toEqual(renewedDomain);
    });

    it('renews domain for specified years', async () => {
      const renewedDomain = {
        ...mockDomainRegistration,
        expires_at: '2027-01-01T00:00:00Z',
        days_until_expiry: 1095,
      };
      vi.mocked(domainsApi.renewDomain).mockResolvedValue(renewedDomain);

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

      let renewData;
      await act(async () => {
        renewData = await result.current.mutateAsync({ id: 1, years: 3 });
      });

      expect(domainsApi.renewDomain).toHaveBeenCalledWith(1, 3);
      expect(renewData).toEqual(renewedDomain);
    });

    it('updates cache with renewed domain data', async () => {
      const renewedDomain = {
        ...mockDomainRegistration,
        expires_at: '2026-01-01T00:00:00Z',
      };
      vi.mocked(domainsApi.getDomainRegistration).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.renewDomain).mockResolvedValue(renewedDomain);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      const { result: domainResult } = renderHook(() => useDomainRegistration(1), {
        wrapper,
      });

      await waitFor(() => {
        expect(domainResult.current.data?.expires_at).toBe('2025-01-01T00:00:00Z');
      });

      const { result: renewResult } = renderHook(() => useRenewDomain(), {
        wrapper,
      });

      let renewData;
      await act(async () => {
        renewData = await renewResult.current.mutateAsync({ id: 1, years: 1 });
      });

      // Verify mutation returned updated data
      expect(renewData?.expires_at).toBe('2026-01-01T00:00:00Z');

      // Refetch to verify cache updated
      await act(async () => {
        await domainResult.current.refetch();
      });
    });

    it('handles renewal errors', async () => {
      vi.mocked(domainsApi.renewDomain).mockRejectedValue(new Error('Renewal failed'));

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

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

      expect(caughtError).toEqual(new Error('Renewal failed'));
    });
  });

  describe('useSyncDomain', () => {
    it('syncs domain info from NameSilo', async () => {
      const syncedDomain = {
        ...mockDomainRegistration,
        expires_at: '2025-06-01T00:00:00Z',
        days_until_expiry: 515,
      };
      vi.mocked(domainsApi.syncDomain).mockResolvedValue(syncedDomain);

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

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

      expect(domainsApi.syncDomain).toHaveBeenCalledWith(1);
      expect(syncData).toEqual(syncedDomain);
    });

    it('updates cache with synced data', async () => {
      const syncedDomain = {
        ...mockDomainRegistration,
        status: 'active' as const,
        expires_at: '2025-12-31T00:00:00Z',
      };
      vi.mocked(domainsApi.getDomainRegistration).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.syncDomain).mockResolvedValue(syncedDomain);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      const { result: domainResult } = renderHook(() => useDomainRegistration(1), {
        wrapper,
      });

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

      expect(domainResult.current.data?.expires_at).toBe('2025-01-01T00:00:00Z');

      const { result: syncResult } = renderHook(() => useSyncDomain(), {
        wrapper,
      });

      let syncData;
      await act(async () => {
        syncData = await syncResult.current.mutateAsync(1);
      });

      // Verify mutation returned updated data
      expect(syncData?.expires_at).toBe('2025-12-31T00:00:00Z');

      // Refetch to verify cache updated
      await act(async () => {
        await domainResult.current.refetch();
      });
    });

    it('invalidates registrations list after sync', async () => {
      vi.mocked(domainsApi.syncDomain).mockResolvedValue(mockDomainRegistration);
      vi.mocked(domainsApi.getRegisteredDomains).mockResolvedValue(mockRegisteredDomains);

      const wrapper = createWrapper();

      const { result: domainsResult } = renderHook(() => useRegisteredDomains(), {
        wrapper,
      });

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

      const { result: syncResult } = renderHook(() => useSyncDomain(), {
        wrapper,
      });

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

      await waitFor(() => {
        expect(domainsApi.getRegisteredDomains).toHaveBeenCalledTimes(2);
      });
    });

    it('handles sync errors', async () => {
      vi.mocked(domainsApi.syncDomain).mockRejectedValue(new Error('Sync failed'));

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

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

      expect(caughtError).toEqual(new Error('Sync failed'));
    });
  });

  // ============================================
  // History
  // ============================================

  describe('useSearchHistory', () => {
    it('fetches search history', async () => {
      vi.mocked(domainsApi.getSearchHistory).mockResolvedValue(mockSearchHistory);

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

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

      expect(domainsApi.getSearchHistory).toHaveBeenCalledTimes(1);
      expect(result.current.data).toEqual(mockSearchHistory);
      expect(result.current.data).toHaveLength(2);
    });

    it('uses staleTime of 1 minute', async () => {
      vi.mocked(domainsApi.getSearchHistory).mockResolvedValue(mockSearchHistory);

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

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

      expect(result.current.dataUpdatedAt).toBeGreaterThan(0);
    });

    it('handles empty search history', async () => {
      vi.mocked(domainsApi.getSearchHistory).mockResolvedValue([]);

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

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

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

    it('handles fetch errors', async () => {
      vi.mocked(domainsApi.getSearchHistory).mockRejectedValue(
        new Error('History fetch failed')
      );

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

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

      expect(result.current.error).toEqual(new Error('History fetch failed'));
    });
  });
});
