import { QueryClientProvider } from '@tanstack/react-query'
import { renderHook, waitFor } from '@testing-library/react'
import { createTestQueryClient } from '../test/utils'
import { useGames, useGame, useGameRoutes, useRoutePokemon } from './useGames'

vi.mock('../api/games')

import { getGames, getGame, getGameRoutes, getRoutePokemon } from '../api/games'

function createWrapper() {
  const queryClient = createTestQueryClient()
  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
  return { queryClient, wrapper }
}

describe('useGames', () => {
  it('calls getGames and returns data', async () => {
    const games = [{ id: 1, name: 'Red' }]
    vi.mocked(getGames).mockResolvedValue(games as never)
    const { wrapper } = createWrapper()

    const { result } = renderHook(() => useGames(), { wrapper })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(getGames).toHaveBeenCalledOnce()
    expect(result.current.data).toEqual(games)
  })
})

describe('useGame', () => {
  it('calls getGame with the given id', async () => {
    const game = { id: 2, name: 'Blue' }
    vi.mocked(getGame).mockResolvedValue(game as never)
    const { wrapper } = createWrapper()

    const { result } = renderHook(() => useGame(2), { wrapper })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(getGame).toHaveBeenCalledWith(2)
  })
})

describe('useGameRoutes', () => {
  it('is disabled when gameId is null', () => {
    const { wrapper } = createWrapper()
    const { result } = renderHook(() => useGameRoutes(null), { wrapper })
    expect(result.current.fetchStatus).toBe('idle')
    expect(getGameRoutes).not.toHaveBeenCalled()
  })

  it('fetches routes when gameId is provided', async () => {
    const routes = [{ id: 10, name: 'Route 1' }]
    vi.mocked(getGameRoutes).mockResolvedValue(routes as never)
    const { wrapper } = createWrapper()

    const { result } = renderHook(() => useGameRoutes(1), { wrapper })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(getGameRoutes).toHaveBeenCalledWith(1, undefined)
    expect(result.current.data).toEqual(routes)
  })

  it('passes allowedTypes to the API', async () => {
    vi.mocked(getGameRoutes).mockResolvedValue([] as never)
    const { wrapper } = createWrapper()

    renderHook(() => useGameRoutes(5, ['grass', 'water']), { wrapper })
    await waitFor(() => expect(getGameRoutes).toHaveBeenCalledWith(5, ['grass', 'water']))
  })
})

describe('useRoutePokemon', () => {
  it('is disabled when routeId is null', () => {
    const { wrapper } = createWrapper()
    const { result } = renderHook(() => useRoutePokemon(null), { wrapper })
    expect(result.current.fetchStatus).toBe('idle')
    expect(getRoutePokemon).not.toHaveBeenCalled()
  })

  it('fetches pokemon for a given route', async () => {
    vi.mocked(getRoutePokemon).mockResolvedValue([] as never)
    const { wrapper } = createWrapper()

    renderHook(() => useRoutePokemon(3, 1), { wrapper })
    await waitFor(() => expect(getRoutePokemon).toHaveBeenCalledWith(3, 1))
  })
})
