import { describe, it, expect, vi } from 'vitest'
import { renderHook, act, waitFor } from '@testing-library/react'
import { useMediaLibrary } from './useMediaLibrary.js'
import type { MediaClient, MediaItem } from './client.js'

const page = (n: number, cursor?: string): { items: MediaItem[]; cursor?: string } => ({
  items: [{ key: `media/${n}`, url: `https://cdn/${n}`, size: 1 }],
  cursor,
})

function client(): MediaClient {
  const list = vi
    .fn<MediaClient['list']>()
    .mockResolvedValueOnce(page(1, 'c1'))
    .mockResolvedValueOnce(page(2, undefined))
  return { upload: async () => page(1).items[0]!, list, remove: vi.fn(async () => {}) }
}

describe('useMediaLibrary', () => {
  it('loads first page, then loadMore appends until hasMore is false', async () => {
    const c = client()
    const { result } = renderHook(() => useMediaLibrary(c))
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(result.current.items).toHaveLength(1)
    expect(result.current.hasMore).toBe(true)
    await act(async () => { await result.current.loadMore() })
    expect(result.current.items).toHaveLength(2)
    expect(result.current.hasMore).toBe(false)
  })

  it('remove drops the item and calls client.remove', async () => {
    const c = client()
    const { result } = renderHook(() => useMediaLibrary(c))
    await waitFor(() => expect(result.current.items).toHaveLength(1))
    await act(async () => { await result.current.remove('media/1') })
    expect(c.remove).toHaveBeenCalledWith('media/1')
    expect(result.current.items.find((i) => i.key === 'media/1')).toBeUndefined()
  })

  it('loadMore called twice before resolve fetches the next page only once (no double-append)', async () => {
    let resolveSecond!: (v: { items: MediaItem[]; cursor?: string }) => void
    const list = vi
      .fn<MediaClient['list']>()
      .mockResolvedValueOnce(page(1, 'c1'))
      .mockImplementationOnce(() => new Promise((res) => { resolveSecond = res }))
    const c: MediaClient = { upload: async () => page(1).items[0]!, list, remove: vi.fn(async () => {}) }
    const { result } = renderHook(() => useMediaLibrary(c))
    await waitFor(() => expect(result.current.loading).toBe(false))
    // two near-simultaneous loadMore calls while the page-2 fetch is unresolved
    await act(async () => {
      void result.current.loadMore()
      void result.current.loadMore()
      resolveSecond(page(2, undefined))
    })
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(list).toHaveBeenCalledTimes(2) // initial + exactly one loadMore, not three
    expect(result.current.items).toHaveLength(2)
    expect(result.current.hasMore).toBe(false)
  })
})
