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 {
  useLocations,
  useLocation,
  useCreateLocation,
  useUpdateLocation,
  useDeleteLocation,
  useSetPrimaryLocation,
  useSetLocationActive,
} from '../useLocations';
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('useLocations hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('useLocations', () => {
    it('fetches locations and returns data', async () => {
      const mockLocations = [
        {
          id: 1,
          name: 'Main Office',
          city: 'Denver',
          state: 'CO',
          is_active: true,
          is_primary: true,
          display_order: 0,
          resource_count: 5,
          service_count: 10,
        },
        {
          id: 2,
          name: 'Branch Office',
          city: 'Boulder',
          state: 'CO',
          is_active: true,
          is_primary: false,
          display_order: 1,
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockLocations });

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

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

      expect(apiClient.get).toHaveBeenCalledWith('/locations/');
      expect(result.current.data).toHaveLength(2);
      expect(result.current.data?.[0]).toEqual(expect.objectContaining({
        id: 1,
        name: 'Main Office',
        is_primary: true,
      }));
    });

    it('fetches all locations when includeInactive is true', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      renderHook(() => useLocations({ includeInactive: true }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(apiClient.get).toHaveBeenCalledWith('/locations/?include_inactive=true');
      });
    });
  });

  describe('useLocation', () => {
    it('fetches a single location by id', async () => {
      const mockLocation = {
        id: 1,
        name: 'Main Office',
        is_active: true,
        is_primary: true,
        display_order: 0,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockLocation });

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

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

      expect(apiClient.get).toHaveBeenCalledWith('/locations/1/');
      expect(result.current.data?.name).toBe('Main Office');
    });

    it('does not fetch when id is undefined', async () => {
      renderHook(() => useLocation(undefined), {
        wrapper: createWrapper(),
      });

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

  describe('useCreateLocation', () => {
    it('creates location with correct data', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1 } });

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

      await act(async () => {
        await result.current.mutateAsync({
          name: 'New Location',
          city: 'Denver',
          state: 'CO',
        });
      });

      expect(apiClient.post).toHaveBeenCalledWith('/locations/', {
        name: 'New Location',
        city: 'Denver',
        state: 'CO',
      });
    });
  });

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

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

      await act(async () => {
        await result.current.mutateAsync({
          id: 1,
          updates: {
            name: 'Updated Office',
            city: 'Boulder',
          },
        });
      });

      expect(apiClient.patch).toHaveBeenCalledWith('/locations/1/', {
        name: 'Updated Office',
        city: 'Boulder',
      });
    });
  });

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

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

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

      expect(apiClient.delete).toHaveBeenCalledWith('/locations/1/');
    });
  });

  describe('useSetPrimaryLocation', () => {
    it('sets location as primary', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, is_primary: true } });

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

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

      expect(apiClient.post).toHaveBeenCalledWith('/locations/1/set_primary/');
    });
  });

  describe('useSetLocationActive', () => {
    it('activates location', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, is_active: true } });

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

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

      expect(apiClient.post).toHaveBeenCalledWith('/locations/1/set_active/', {
        is_active: true,
      });
    });

    it('deactivates location', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, is_active: false } });

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

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

      expect(apiClient.post).toHaveBeenCalledWith('/locations/1/set_active/', {
        is_active: false,
      });
    });
  });
});
