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

afterEach(() => {
  delete process.env.NEXT_PUBLIC_API_URL
  delete process.env.NEXT_PUBLIC_CARPLACE_DIRECT_API
  delete process.env.API_INTERNAL_URL
  vi.restoreAllMocks()
  Reflect.deleteProperty(globalThis, 'window')
  Reflect.deleteProperty(globalThis, 'fetch')
  vi.resetModules()
})

describe('carplace API helpers', () => {
  it('unwraps successful GET responses from the data envelope', async () => {
    const fetchMock = vi.fn(async () => ({ ok: true, json: async () => ({ data: [{ id: 'vehicle_1' }] }) }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { carplaceFetch } = await import('./api')
    await expect(carplaceFetch('/carplace/vehicles')).resolves.toEqual([{ id: 'vehicle_1' }])
    expect(fetchMock).toHaveBeenCalledWith(expect.stringMatching(/\/api\/v1\/carplace\/vehicles$/), {
      cache: 'no-store',
      credentials: 'include',
    })
  })

  it('throws rich carplace API errors', async () => {
    Object.defineProperty(globalThis, 'fetch', {
      configurable: true,
      value: vi.fn(async () => ({
        ok: false,
        status: 409,
        json: async () => ({ message: 'Vehicle unavailable', error: 'VEHICLE_UNAVAILABLE', nextAvailableAt: '2026-07-01T10:00:00.000Z' }),
      })),
    })

    const { carplaceFetch } = await import('./api')
    await expect(carplaceFetch('/carplace/book')).rejects.toMatchObject({
      name: 'CarplaceApiError',
      message: 'Vehicle unavailable',
      status: 409,
      code: 'VEHICLE_UNAVAILABLE',
      nextAvailableAt: '2026-07-01T10:00:00.000Z',
    })
  })

  it('returns fallbacks when GET requests fail', async () => {
    Object.defineProperty(globalThis, 'fetch', {
      configurable: true,
      value: vi.fn(async () => ({ ok: false, status: 500, json: async () => ({ message: 'Down' }) })),
    })

    const { carplaceFetchOrDefault } = await import('./api')
    await expect(carplaceFetchOrDefault('/carplace/homepage', { hero: null })).resolves.toEqual({ hero: null })
  })

  it('posts JSON payloads and unwraps successful responses', async () => {
    process.env.NEXT_PUBLIC_API_URL = 'https://api.example.com/api/v1'
    process.env.NEXT_PUBLIC_CARPLACE_DIRECT_API = 'true'
    Object.defineProperty(globalThis, 'window', { configurable: true, value: {} })
    const fetchMock = vi.fn(async () => ({ ok: true, json: async () => ({ data: { id: 'reservation_1' } }) }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { carplacePost } = await import('./api')
    await expect(carplacePost('/site/reservations', { vehicleId: 'vehicle_1' })).resolves.toEqual({ id: 'reservation_1' })
    expect(fetchMock).toHaveBeenCalledWith('https://api.example.com/api/v1/site/reservations', expect.objectContaining({
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ vehicleId: 'vehicle_1' }),
    }))
  })

  it('uses a generic error message when the response body is not JSON', async () => {
    Object.defineProperty(globalThis, 'fetch', {
      configurable: true,
      value: vi.fn(async () => ({ ok: false, status: 502, json: async () => { throw new Error('bad json') } })),
    })

    const { carplaceFetch } = await import('./api')
    await expect(carplaceFetch('/site/homepage')).rejects.toMatchObject({
      name: 'CarplaceApiError',
      message: 'Request failed',
      status: 502,
    })
  })
})
