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(),
    patch: vi.fn(),
    delete: vi.fn(),
  },
}));

import {
  useResources,
  useResource,
  useCreateResource,
  useUpdateResource,
  useDeleteResource,
} from '../useResources';
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('useResources hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('useResources', () => {
    it('fetches resources and transforms data', async () => {
      const mockResources = [
        { id: 1, name: 'Room 1', type: 'ROOM', max_concurrent_events: 2 },
        { id: 2, name: 'Staff 1', type: 'STAFF', user_id: 10 },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResources });

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

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

      expect(apiClient.get).toHaveBeenCalledWith('/resources/?');
      expect(result.current.data).toHaveLength(2);
      expect(result.current.data?.[0]).toEqual({
        id: '1',
        name: 'Room 1',
        type: 'ROOM',
        userId: undefined,
        maxConcurrentEvents: 2,
        savedLaneCount: undefined,
        userCanEditSchedule: false,
        locationId: null,
        locationName: null,
        isMobile: false,
      });
    });

    it('applies type filter', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      renderHook(() => useResources({ type: 'STAFF' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(apiClient.get).toHaveBeenCalledWith('/resources/?type=STAFF');
      });
    });
  });

  describe('useResource', () => {
    it('fetches single resource by id', async () => {
      const mockResource = {
        id: 1,
        name: 'Room 1',
        type: 'ROOM',
        max_concurrent_events: 1,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResource });

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

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

      expect(apiClient.get).toHaveBeenCalledWith('/resources/1/');
      expect(result.current.data?.name).toBe('Room 1');
    });

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

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

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

  describe('useCreateResource', () => {
    it('creates resource with backend field mapping', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1 } });

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

      await act(async () => {
        await result.current.mutateAsync({
          name: 'New Room',
          type: 'ROOM',
          maxConcurrentEvents: 3,
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/resources/', {
        name: 'New Room',
        type: 'ROOM',
        user_id: null,
        max_concurrent_events: 3,
      });
    });

    it('converts userId to user_id integer', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1 } });

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

      await act(async () => {
        await result.current.mutateAsync({
          name: 'Staff',
          type: 'STAFF',
          userId: '42',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/resources/', expect.objectContaining({
        user_id: 42,
      }));
    });
  });

  describe('useUpdateResource', () => {
    it('updates resource with mapped fields', async () => {
      vi.mocked(apiClient.patch).mockResolvedValue({ data: { id: 1 } });

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

      await act(async () => {
        await result.current.mutateAsync({
          id: '1',
          updates: { name: 'Updated Room', maxConcurrentEvents: 5 },
        });
      });

      expect(apiClient.patch).toHaveBeenCalledWith('/resources/1/', {
        name: 'Updated Room',
        max_concurrent_events: 5,
      });
    });

    it('handles userId update', async () => {
      vi.mocked(apiClient.patch).mockResolvedValue({ data: {} });

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

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

      expect(apiClient.patch).toHaveBeenCalledWith('/resources/1/', {
        user: 10,
      });
    });

    it('sets user to null when userId is empty', async () => {
      vi.mocked(apiClient.patch).mockResolvedValue({ data: {} });

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

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

      expect(apiClient.patch).toHaveBeenCalledWith('/resources/1/', {
        user: null,
      });
    });
  });

  describe('useDeleteResource', () => {
    it('deletes resource by id', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({});

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

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

      expect(apiClient.delete).toHaveBeenCalledWith('/resources/5/');
    });
  });
});
