import { afterEach, describe, expect, it, jest } from '@jest/globals'

import { api } from './api'

function makeResponse(body: string, status: number, contentType: string) {
  return {
    ok: status >= 200 && status < 300,
    status,
    headers: {
      get(name: string) {
        return name.toLowerCase() === 'content-type' ? contentType : null
      },
    },
    async json() {
      return JSON.parse(body)
    },
    async text() {
      return body
    },
  }
}

describe('api helper', () => {
  afterEach(() => {
    jest.restoreAllMocks()
    ;(globalThis.fetch as jest.Mock | undefined)?.mockReset?.()
  })

  it('returns parsed json when content-type is json', async () => {
    const fetchMock = jest.fn(async (..._args: any[]) => makeResponse(JSON.stringify({ ok: true }), 200, 'application/json') as any)
    Object.defineProperty(globalThis, 'fetch', { configurable: true, writable: true, value: fetchMock })

    const res = await api<{ ok: boolean }>('/api/healthz')
    expect(res.ok).toBe(true)
    expect(fetchMock).toHaveBeenCalledWith('/api/healthz', { credentials: 'include' })
  })

  it('parses json from text payload when response header is not json', async () => {
    const fetchMock = jest.fn(async (..._args: any[]) => makeResponse('{"value":42}', 200, 'text/plain') as any)
    Object.defineProperty(globalThis, 'fetch', { configurable: true, writable: true, value: fetchMock })

    const res = await api<{ value: number }>('/api/text-json')
    expect(res.value).toBe(42)
  })

  it('returns raw text when text is not json', async () => {
    const fetchMock = jest.fn(async (..._args: any[]) => makeResponse('hello', 200, 'text/plain') as any)
    Object.defineProperty(globalThis, 'fetch', { configurable: true, writable: true, value: fetchMock })

    const res = await api<string>('/api/text')
    expect(res).toBe('hello')
  })

  it('throws server message when response is not ok', async () => {
    const fetchMock = jest.fn(async (..._args: any[]) => makeResponse('invalid credentials', 401, 'text/plain') as any)
    Object.defineProperty(globalThis, 'fetch', { configurable: true, writable: true, value: fetchMock })

    await expect(api('/api/login')).rejects.toThrow('invalid credentials')
  })
})
