import { describe, it, expect, vi } from 'vitest'
import { renderHook, act, waitFor } from '@testing-library/react'
import { useSubmissions } from './useSubmissions.js'
import type { SubmissionClient, StoredSubmission } from './seams.js'

function sub(id: string, spam = false): StoredSubmission {
  return { id, formId: 'f1', data: {}, createdAt: '2026-01-01T00:00:00.000Z', spam }
}
function fakeClient(over: Partial<SubmissionClient> = {}): SubmissionClient {
  return { list: vi.fn(async () => ({ items: [sub('a'), sub('b')] })), get: vi.fn(async () => null), delete: vi.fn(async () => {}), ...over }
}

describe('useSubmissions', () => {
  it('loads on mount', async () => {
    const { result } = renderHook(() => useSubmissions(fakeClient(), 'f1'))
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(result.current.items.map((i) => i.id)).toEqual(['a', 'b'])
  })
  it('hasMore reflects nextCursor and loadMore appends', async () => {
    const list = vi.fn().mockResolvedValueOnce({ items: [sub('a')], nextCursor: 'c1' }).mockResolvedValueOnce({ items: [sub('b')] })
    const { result } = renderHook(() => useSubmissions(fakeClient({ list }), 'f1'))
    await waitFor(() => expect(result.current.hasMore).toBe(true))
    await act(async () => { await result.current.loadMore() })
    expect(result.current.items.map((i) => i.id)).toEqual(['a', 'b'])
    expect(result.current.hasMore).toBe(false)
  })
  it('remove is optimistic and rolls back on rejection (awaitable reject)', async () => {
    const del = vi.fn().mockRejectedValue(new Error('net'))
    const { result } = renderHook(() => useSubmissions(fakeClient({ delete: del }), 'f1'))
    await waitFor(() => expect(result.current.items.length).toBe(2))
    let rejected = false
    await act(async () => { await result.current.remove('a').catch(() => { rejected = true }) })
    expect(rejected).toBe(true)
    expect(result.current.items.map((i) => i.id)).toEqual(['a', 'b']) // rolled back
    expect(result.current.error).not.toBeNull()
  })
  it('captures list error without rejecting; reload retries', async () => {
    const list = vi.fn().mockRejectedValueOnce(new Error('boom')).mockResolvedValue({ items: [sub('a')] })
    const { result } = renderHook(() => useSubmissions(fakeClient({ list }), 'f1'))
    await waitFor(() => expect(result.current.error).not.toBeNull())
    act(() => result.current.reload())
    await waitFor(() => expect(result.current.error).toBeNull())
  })
  it('passes includeSpam through to the client', async () => {
    const list = vi.fn(async () => ({ items: [sub('a', true)] }))
    renderHook(() => useSubmissions(fakeClient({ list }), 'f1', { includeSpam: true }))
    await waitFor(() => expect(list).toHaveBeenCalledWith('f1', expect.objectContaining({ includeSpam: true })))
  })
})
