import { QueryClientProvider } from '@tanstack/react-query'
import { renderHook, waitFor } from '@testing-library/react'
import { createTestQueryClient } from '../test/utils'
import {
  usePokemon,
  usePokemonFamilies,
  usePokemonEncounterLocations,
  usePokemonEvolutionChain,
} from './usePokemon'

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

import {
  getPokemon,
  fetchPokemonFamilies,
  fetchPokemonEncounterLocations,
  fetchPokemonEvolutionChain,
} from '../api/pokemon'

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

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

  it('fetches a pokemon by id', async () => {
    const mon = { id: 25, name: 'pikachu' }
    vi.mocked(getPokemon).mockResolvedValue(mon as never)
    const { wrapper } = createWrapper()

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

    expect(getPokemon).toHaveBeenCalledWith(25)
    expect(result.current.data).toEqual(mon)
  })
})

describe('usePokemonFamilies', () => {
  it('calls fetchPokemonFamilies and returns data', async () => {
    const families = [{ id: 1, members: [] }]
    vi.mocked(fetchPokemonFamilies).mockResolvedValue(families as never)
    const { wrapper } = createWrapper()

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

    expect(fetchPokemonFamilies).toHaveBeenCalledOnce()
    expect(result.current.data).toEqual(families)
  })
})

describe('usePokemonEncounterLocations', () => {
  it('is disabled when pokemonId is null', () => {
    const { wrapper } = createWrapper()
    const { result } = renderHook(() => usePokemonEncounterLocations(null), { wrapper })
    expect(result.current.fetchStatus).toBe('idle')
  })

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

    renderHook(() => usePokemonEncounterLocations(25), { wrapper })
    await waitFor(() => expect(fetchPokemonEncounterLocations).toHaveBeenCalledWith(25))
  })
})

describe('usePokemonEvolutionChain', () => {
  it('is disabled when pokemonId is null', () => {
    const { wrapper } = createWrapper()
    const { result } = renderHook(() => usePokemonEvolutionChain(null), { wrapper })
    expect(result.current.fetchStatus).toBe('idle')
  })

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

    renderHook(() => usePokemonEvolutionChain(4), { wrapper })
    await waitFor(() => expect(fetchPokemonEvolutionChain).toHaveBeenCalledWith(4))
  })
})
