import { StrictMode } from 'react'
import { render, renderHook, screen, waitFor } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import type { Cart } from '@platform-modules/commerce-cart'
import { CartProvider } from './CartProvider.js'
import { useCartContext } from './CartProvider.js'

const aCart = (over: Partial<Cart> = {}): Cart => ({ id: 'c1', currency: 'USD', lines: [], subtotal: 0n, ...over })
const clientWith = (cart: Cart) => ({
  getCart: vi.fn(async () => cart),
  apply: vi.fn(async () => cart),
  merge: vi.fn(async () => cart),
})

// Inline probe (does not depend on Task 6 hooks).
function Probe() {
  const { cart, loading } = useCartContext('Probe')
  return <div data-testid="probe">{loading ? 'loading' : `cart:${cart?.id ?? 'none'}`}</div>
}

describe('CartProvider', () => {
  it('mount-fetches the cart via the client when not seeded', async () => {
    const client = clientWith(aCart({ id: 'fetched' }))
    render(<CartProvider client={client}><Probe /></CartProvider>)
    expect(screen.getByTestId('probe').textContent).toBe('loading')
    await waitFor(() => expect(screen.getByTestId('probe').textContent).toBe('cart:fetched'))
    expect(client.getCart).toHaveBeenCalledTimes(1)
  })

  it('seeded: no mount-fetch, loading:false immediately', async () => {
    const client = clientWith(aCart({ id: 'fetched' }))
    render(<CartProvider client={client} initialCart={aCart({ id: 'seed' })}><Probe /></CartProvider>)
    expect(screen.getByTestId('probe').textContent).toBe('cart:seed')
    // give any erroneous mount effect a tick to fire
    await new Promise((r) => setTimeout(r, 0))
    expect(client.getCart).not.toHaveBeenCalled()
  })

  it('seeded + StrictMode double-invoke does NOT fetch over the seed (settled-key, not a flag)', async () => {
    const client = clientWith(aCart({ id: 'fetched' }))
    render(
      <StrictMode>
        <CartProvider client={client} initialCart={aCart({ id: 'seed' })}><Probe /></CartProvider>
      </StrictMode>,
    )
    expect(screen.getByTestId('probe').textContent).toBe('cart:seed')
    await new Promise((r) => setTimeout(r, 0))
    expect(client.getCart).not.toHaveBeenCalled()
  })

  it('reload() refetches even when seeded', async () => {
    const client = clientWith(aCart({ id: 'fetched' }))
    const { result } = renderHook(() => useCartContext('t'), {
      wrapper: ({ children }) => <CartProvider client={client} initialCart={aCart({ id: 'seed' })}>{children}</CartProvider>,
    })
    expect(result.current.cart?.id).toBe('seed')
    result.current.reload()
    await waitFor(() => expect(result.current.cart?.id).toBe('fetched'))
    expect(client.getCart).toHaveBeenCalledTimes(1)
  })

  it('surfaces a load error', async () => {
    const client = { ...clientWith(aCart()), getCart: vi.fn(async () => { throw new Error('boom') }) }
    const { result } = renderHook(() => useCartContext('t'), {
      wrapper: ({ children }) => <CartProvider client={client}>{children}</CartProvider>,
    })
    await waitFor(() => expect(result.current.error?.message).toBe('boom'))
    expect(result.current.loading).toBe(false)
  })

  it('useCartContext throws CartProviderError outside a provider', () => {
    expect(() => renderHook(() => useCartContext('useCart'))).toThrowError(/within <CartProvider>/)
  })
})