import { QueryClientProvider } from '@tanstack/react-query'
import { renderHook, waitFor, act } from '@testing-library/react'
import { createTestQueryClient } from '../test/utils'
import {
  useGameBosses,
  useBossResults,
  useCreateBossResult,
  useDeleteBossResult,
} from './useBosses'

vi.mock('../api/bosses')
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))

import { getGameBosses, getBossResults, createBossResult, deleteBossResult } from '../api/bosses'

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

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

  it('fetches bosses for a given game', async () => {
    const bosses = [{ id: 1, name: 'Brock' }]
    vi.mocked(getGameBosses).mockResolvedValue(bosses as never)
    const { wrapper } = createWrapper()

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

    expect(getGameBosses).toHaveBeenCalledWith(1, undefined)
    expect(result.current.data).toEqual(bosses)
  })

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

    renderHook(() => useGameBosses(2, true), { wrapper })
    await waitFor(() => expect(getGameBosses).toHaveBeenCalledWith(2, true))
  })
})

describe('useBossResults', () => {
  it('fetches boss results for a given run', async () => {
    vi.mocked(getBossResults).mockResolvedValue([] as never)
    const { wrapper } = createWrapper()

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

    expect(getBossResults).toHaveBeenCalledWith(10)
  })
})

describe('useCreateBossResult', () => {
  it('calls createBossResult with the run id and input', async () => {
    vi.mocked(createBossResult).mockResolvedValue({} as never)
    const { wrapper } = createWrapper()

    const { result } = renderHook(() => useCreateBossResult(5), { wrapper })
    const input = { bossId: 1, won: true }
    await act(async () => {
      await result.current.mutateAsync(input as never)
    })

    expect(createBossResult).toHaveBeenCalledWith(5, input)
  })

  it('invalidates boss results for the run on success', async () => {
    vi.mocked(createBossResult).mockResolvedValue({} as never)
    const { queryClient, wrapper } = createWrapper()
    const spy = vi.spyOn(queryClient, 'invalidateQueries')

    const { result } = renderHook(() => useCreateBossResult(5), { wrapper })
    await act(async () => {
      await result.current.mutateAsync({} as never)
    })

    expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 5, 'boss-results'] })
  })
})

describe('useDeleteBossResult', () => {
  it('calls deleteBossResult with the run id and result id', async () => {
    vi.mocked(deleteBossResult).mockResolvedValue(undefined as never)
    const { wrapper } = createWrapper()

    const { result } = renderHook(() => useDeleteBossResult(5), { wrapper })
    await act(async () => {
      await result.current.mutateAsync(99)
    })

    expect(deleteBossResult).toHaveBeenCalledWith(5, 99)
  })

  it('invalidates boss results for the run on success', async () => {
    vi.mocked(deleteBossResult).mockResolvedValue(undefined as never)
    const { queryClient, wrapper } = createWrapper()
    const spy = vi.spyOn(queryClient, 'invalidateQueries')

    const { result } = renderHook(() => useDeleteBossResult(5), { wrapper })
    await act(async () => {
      await result.current.mutateAsync(99)
    })

    expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 5, 'boss-results'] })
  })
})
