import { describe, it, expect, vi } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useCartList, useCartDetail, useCartCreate, useCartUpdate, useCartDelete } from './useCart';
import type { CartPort } from '../ports/CartPort';

const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('Cart Hooks', () => {
  const mockPort: CartPort = {
    getAll: vi.fn(),
    getById: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
  };

  describe('useCartList', () => {
    it('should fetch all entities', async () => {
      const mockEntities = [{ id: '1' }];
      mockPort.getAll.mockResolvedValueOnce(mockEntities);

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

      expect(result.current.isLoading).toBe(true);
      await act(async () => {
        await result.current.refetch();
      });
      expect(result.current.data).toEqual(mockEntities);
    });
  });

  describe('useCartDetail', () => {
    it('should fetch entity by id', async () => {
      const mockEntity = { id: '1' };
      mockPort.getById.mockResolvedValueOnce(mockEntity);

      const { result } = renderHook(() => useCartDetail(mockPort, '1'), {
        wrapper: createWrapper(),
      });

      expect(result.current.isLoading).toBe(true);
      await act(async () => {
        await result.current.refetch();
      });
      expect(result.current.data).toEqual(mockEntity);
    });
  });

  describe('useCartCreate', () => {
    it('should create entity', async () => {
      const mockEntity = { id: '1' };
      mockPort.create.mockResolvedValueOnce(mockEntity);

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

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

      expect(mockPort.create).toHaveBeenCalledWith({ id: '1' });
    });
  });

  describe('useCartUpdate', () => {
    it('should update entity', async () => {
      const mockEntity = { id: '1' };
      mockPort.update.mockResolvedValueOnce(mockEntity);

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

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

      expect(mockPort.update).toHaveBeenCalledWith('1', { id: '1' });
    });
  });

  describe('useCartDelete', () => {
    it('should delete entity', async () => {
      mockPort.delete.mockResolvedValueOnce();

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

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

      expect(mockPort.delete).toHaveBeenCalledWith('1');
    });
  });
});