import { renderHook, act, waitFor } from '@testing-library/react';
import { useCompanies } from '@/hooks/useCompanies';
import { getAvailableCompanies } from '@/lib/api/csv-rates';

jest.mock('@/lib/api/csv-rates', () => ({
  getAvailableCompanies: jest.fn(),
}));

const mockGetAvailableCompanies = jest.mocked(getAvailableCompanies);

const MOCK_COMPANIES = ['Maersk', 'MSC', 'CMA CGM', 'Hapag-Lloyd'];

beforeEach(() => {
  jest.clearAllMocks();
});

describe('useCompanies', () => {
  describe('initial state', () => {
    it('starts with loading=true', () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });
      const { result } = renderHook(() => useCompanies());
      expect(result.current.loading).toBe(true);
    });

    it('starts with an empty companies array', () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });
      const { result } = renderHook(() => useCompanies());
      expect(result.current.companies).toEqual([]);
    });

    it('starts with error=null', () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });
      const { result } = renderHook(() => useCompanies());
      expect(result.current.error).toBeNull();
    });
  });

  describe('on mount — success', () => {
    it('fetches companies automatically on mount', async () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });

      renderHook(() => useCompanies());

      await waitFor(() => {
        expect(mockGetAvailableCompanies).toHaveBeenCalledTimes(1);
      });
    });

    it('populates companies after a successful fetch', async () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });

      const { result } = renderHook(() => useCompanies());

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

      expect(result.current.companies).toEqual(MOCK_COMPANIES);
      expect(result.current.error).toBeNull();
    });

    it('handles an empty companies list', async () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: [], total: 0 });

      const { result } = renderHook(() => useCompanies());

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

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

  describe('on mount — error', () => {
    it('sets error when the API call fails', async () => {
      mockGetAvailableCompanies.mockRejectedValue(new Error('Service unavailable'));

      const { result } = renderHook(() => useCompanies());

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

      expect(result.current.error).toBe('Service unavailable');
      expect(result.current.companies).toEqual([]);
    });

    it('uses a default error message when the error has no message', async () => {
      mockGetAvailableCompanies.mockRejectedValue({});

      const { result } = renderHook(() => useCompanies());

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

      expect(result.current.error).toBe('Failed to fetch companies');
    });
  });

  describe('refetch', () => {
    it('exposes a refetch function', async () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });

      const { result } = renderHook(() => useCompanies());

      await waitFor(() => expect(result.current.loading).toBe(false));

      expect(typeof result.current.refetch).toBe('function');
    });

    it('re-triggers the API call when refetch is invoked', async () => {
      mockGetAvailableCompanies.mockResolvedValue({ companies: MOCK_COMPANIES, total: 4 });

      const { result } = renderHook(() => useCompanies());

      await waitFor(() => expect(result.current.loading).toBe(false));

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

      expect(mockGetAvailableCompanies).toHaveBeenCalledTimes(2);
    });

    it('updates companies with fresh data on refetch', async () => {
      mockGetAvailableCompanies
        .mockResolvedValueOnce({ companies: ['Maersk'], total: 1 })
        .mockResolvedValueOnce({ companies: ['Maersk', 'MSC'], total: 2 });

      const { result } = renderHook(() => useCompanies());

      await waitFor(() => expect(result.current.companies).toEqual(['Maersk']));

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

      expect(result.current.companies).toEqual(['Maersk', 'MSC']);
    });
  });
});
