import { beforeEach, describe, expect, it, vi } from 'vitest'

const mocks = vi.hoisted (() => {
  const client = {
    delete: vi.fn (),
    get:    vi.fn (),
    patch:  vi.fn (),
    post:   vi.fn (),
    put:    vi.fn (),
  }

  return {
    client,
    isAxiosError: vi.fn (),
  }
})

vi.mock ('axios', () => ({
  default: {
    create:       vi.fn (() => mocks.client),
    isAxiosError: mocks.isAxiosError,
  },
}))

vi.mock ('@/config', () => ({
  API_BASE_URL: '/api',
}))

describe ('api helpers', () => {
  beforeEach (() => {
    vi.clearAllMocks ()
    localStorage.clear ()
  })

  it ('adds the transfer code header and camelizes get responses', async () => {
    localStorage.setItem ('user_code', 'abc123')
    mocks.client.get.mockResolvedValueOnce ({
      data: { post_id: 1, nested_value: { created_at: 'now' } },
    })

    const { apiGet } = await import ('@/lib/api')
    const data = await apiGet<{ postId: number; nestedValue: { createdAt: string } }> (
        '/posts/1',
        { headers: { 'X-Extra': '1' }, params: { page: 2 } },
    )

    expect (mocks.client.get).toHaveBeenCalledWith (
        '/posts/1',
        {
          headers: { 'X-Transfer-Code': 'abc123', 'X-Extra': '1' },
          params:  { page: 2 },
        },
    )
    expect (data).toEqual ({ postId: 1, nestedValue: { createdAt: 'now' } })
  })

  it ('passes an empty body for post-like requests when body is omitted', async () => {
    mocks.client.patch.mockResolvedValueOnce ({ data: { ok_value: true } })

    const { apiPatch } = await import ('@/lib/api')
    const data = await apiPatch<{ okValue: boolean }> ('/posts/1')

    expect (mocks.client.patch).toHaveBeenCalledWith (
        '/posts/1',
        {},
        { headers: { 'X-Transfer-Code': '' } },
    )
    expect (data.okValue).toBe (true)
  })

  it ('does not camelize blob responses', async () => {
    const blob = new Blob (['csv'])
    mocks.client.get.mockResolvedValueOnce ({ data: blob })

    const { apiGet } = await import ('@/lib/api')
    const data = await apiGet<Blob> ('/exports', { responseType: 'blob' })

    expect (data).toBe (blob)
  })

  it ('delegates deletes and exposes axios error detection', async () => {
    const err = new Error ('bad')
    mocks.client.delete.mockResolvedValueOnce ({})
    mocks.isAxiosError.mockReturnValueOnce (true)

    const { apiDelete, isApiError } = await import ('@/lib/api')
    await apiDelete ('/posts/1')

    expect (mocks.client.delete).toHaveBeenCalledWith (
        '/posts/1',
        { headers: { 'X-Transfer-Code': '' } },
    )
    expect (isApiError (err)).toBe (true)
    expect (mocks.isAxiosError).toHaveBeenCalledWith (err)
  })
})
