import { describe, it, expect, vi } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useUserList, useUserDetail, useUserCreate, useUserUpdate, useUserDelete } from './useUser';
import type { UserPort } from '../ports/UserPort';

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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