import { renderHook, waitFor } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import type { ReactNode } from 'react'
import type { Order, Page } from '@platform-modules/commerce-orders'
import { MarketplaceProvider } from './MarketplaceProvider.js'
import { useVendorOrders } from './useVendorOrders.js'
import type { MarketplaceClient } from './client.js'

const wirePage = { items: [], nextCursor: null }

function page(): Page<Order> {
  return { items: [], nextCursor: null }
}

function wrap(client: MarketplaceClient) {
  return ({ children }: { children: ReactNode }) => (
    <MarketplaceProvider client={client}>{children}</MarketplaceProvider>
  )
}

function baseClient(overrides: Partial<MarketplaceClient> = {}): MarketplaceClient {
  return {
    getVendorProfile: vi.fn(),
    getVendorOrders: vi.fn(async () => wirePage),
    applyAsVendor: vi.fn(),
    createVendorProduct: vi.fn(),
    updateVendorProduct: vi.fn(),
    ...overrides,
  }
}

describe('useVendorOrders', () => {
  it('seed with initialData → data equals seeded page, client NOT called', () => {
    const getVendorOrders = vi.fn()
    const client = baseClient({ getVendorOrders })
    const seeded = page()
    const { result } = renderHook(() => useVendorOrders(undefined, { initialData: seeded }), {
      wrapper: wrap(client),
    })
    expect(result.current.page).toEqual(seeded)
    expect(getVendorOrders).not.toHaveBeenCalled()
  })

  it('no seed → client.getVendorOrders called on mount, data = revived page', async () => {
    const getVendorOrders = vi.fn(async () => wirePage)
    const client = baseClient({ getVendorOrders })
    const { result } = renderHook(() => useVendorOrders(), { wrapper: wrap(client) })
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(getVendorOrders).toHaveBeenCalledTimes(1)
    expect(getVendorOrders).toHaveBeenCalledWith(undefined)
    expect(result.current.page).toEqual(page())
  })

  it('filter key is VALUE-based: same value, new object → client NOT called again', async () => {
    const getVendorOrders = vi.fn(async () => wirePage)
    const client = baseClient({ getVendorOrders })
    const { rerender } = renderHook(({ f }: { f: { limit: number } }) => useVendorOrders(f), {
      wrapper: wrap(client),
      initialProps: { f: { limit: 10 } },
    })
    await waitFor(() => expect(getVendorOrders).toHaveBeenCalledTimes(1))
    rerender({ f: { limit: 10 } })
    await waitFor(() => expect(getVendorOrders).toHaveBeenCalledTimes(1))
  })

  it('filter change → different value → client called again', async () => {
    const getVendorOrders = vi.fn(async () => wirePage)
    const client = baseClient({ getVendorOrders })
    const { rerender } = renderHook(({ f }: { f: { limit: number } }) => useVendorOrders(f), {
      wrapper: wrap(client),
      initialProps: { f: { limit: 10 } },
    })
    await waitFor(() => expect(getVendorOrders).toHaveBeenCalledTimes(1))
    rerender({ f: { limit: 20 } })
    await waitFor(() => expect(getVendorOrders).toHaveBeenCalledTimes(2))
  })

  it('getVendorOrders rejects → error is set', async () => {
    const client = baseClient({
      getVendorOrders: vi.fn(async () => {
        throw new Error('boom')
      }),
    })
    const { result } = renderHook(() => useVendorOrders(), { wrapper: wrap(client) })
    await waitFor(() => expect(result.current.error).toBeTruthy())
    expect(result.current.error?.message).toBe('boom')
    expect(result.current.page).toBeNull()
  })

  it('reload() triggers another call', async () => {
    const getVendorOrders = vi.fn(async () => wirePage)
    const client = baseClient({ getVendorOrders })
    const { result } = renderHook(() => useVendorOrders(), { wrapper: wrap(client) })
    await waitFor(() => expect(result.current.loading).toBe(false))
    result.current.reload()
    await waitFor(() => expect(getVendorOrders).toHaveBeenCalledTimes(2))
  })
})
