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

function installBrowser(token?: string) {
  Object.defineProperty(globalThis, 'window', {
    configurable: true,
    value: {
      location: {
        hostname: 'localhost',
      },
      localStorage: {
        getItem: vi.fn(() => token ?? null),
      },
    },
  })
}

function setBrowserHostname(hostname: string) {
  ;(globalThis.window as any).location.hostname = hostname
}

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

describe('dashboard apiFetch', () => {
  it('adds JSON headers and sends cookies for browser requests', async () => {
    installBrowser()
    const fetchMock = vi.fn(async () => ({
      ok: true,
      json: async () => ({ data: { ok: true } }),
    }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await expect(apiFetch('/team')).resolves.toEqual({ ok: true })

    expect(fetchMock).toHaveBeenCalledWith(expect.stringMatching(/\/api\/v1\/team$/), expect.objectContaining({
      credentials: 'include',
      headers: expect.objectContaining({
        'Content-Type': 'application/json',
      }),
    }))
  })

  it('normalizes a public API origin to the versioned API base', async () => {
    installBrowser()
    process.env.NEXT_PUBLIC_API_URL = 'https://api.rentaldrivego.ma'
    const fetchMock = vi.fn(async () => ({
      ok: true,
      json: async () => ({ data: { ok: true } }),
    }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await apiFetch('/auth/account/start', { method: 'POST', body: JSON.stringify({}) })

    expect(fetchMock).toHaveBeenCalledWith('https://api.rentaldrivego.ma/api/v1/auth/account/start', expect.any(Object))
  })

  it('uses the dashboard proxy on production hosts when the public API URL is cross-host', async () => {
    installBrowser()
    setBrowserHostname('rentaldrivego.ma')
    process.env.NEXT_PUBLIC_API_URL = 'https://api.rentaldrivego.ma/api/v1'
    const fetchMock = vi.fn(async () => ({
      ok: true,
      json: async () => ({ data: { ok: true } }),
    }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await apiFetch('/auth/employee/login', { method: 'POST', body: JSON.stringify({}) })

    expect(fetchMock).toHaveBeenCalledWith('/dashboard/api/v1/auth/employee/login', expect.objectContaining({
      credentials: 'include',
    }))
  })

  it.each([
    ['https://api.rentaldrivego.ma', 'https://api.rentaldrivego.ma/api/v1/auth/account/start'],
    ['https://api.rentaldrivego.ma/', 'https://api.rentaldrivego.ma/api/v1/auth/account/start'],
    ['https://api.rentaldrivego.ma/api', 'https://api.rentaldrivego.ma/api/v1/auth/account/start'],
    ['https://api.rentaldrivego.ma/api/v1', 'https://api.rentaldrivego.ma/api/v1/auth/account/start'],
    ['/dashboard/api/v1', '/dashboard/api/v1/auth/account/start'],
  ])('normalizes API base %s', async (configuredBase, expectedUrl) => {
    installBrowser()
    process.env.NEXT_PUBLIC_API_URL = configuredBase
    const fetchMock = vi.fn(async () => ({
      ok: true,
      json: async () => ({ data: { ok: true } }),
    }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await apiFetch('/auth/account/start', { method: 'POST', body: JSON.stringify({}) })

    expect(fetchMock).toHaveBeenCalledWith(expectedUrl, expect.any(Object))
  })

  it('falls back to the dashboard proxy when no public API URL is configured', async () => {
    installBrowser()
    const fetchMock = vi.fn(async () => ({
      ok: true,
      json: async () => ({ data: { ok: true } }),
    }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await apiFetch('/auth/account/start')

    expect(fetchMock).toHaveBeenCalledWith('/dashboard/api/v1/auth/account/start', expect.any(Object))
  })

  it('does not add a dashboard prefix when an absolute API origin is configured', async () => {
    installBrowser()
    process.env.NEXT_PUBLIC_API_URL = 'https://api.rentaldrivego.ma/api/v1'
    const fetchMock = vi.fn(async () => ({
      ok: true,
      json: async () => ({ data: { ok: true } }),
    }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await apiFetch('/auth/account/start')

    const calledUrl = (fetchMock as any).mock.calls[0]?.[0]
    expect(calledUrl).toBe('https://api.rentaldrivego.ma/api/v1/auth/account/start')
    expect(calledUrl).not.toContain('/dashboard')
    expect(calledUrl).not.toContain('/api/v1/api/v1')
  })

  it('does not force JSON content type for FormData payloads', async () => {
    installBrowser()
    const fetchMock = vi.fn(async () => ({ ok: true, json: async () => ({ data: { uploaded: true } }) }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetch } = await import('./api')
    await apiFetch('/uploads', { method: 'POST', body: new FormData() })

    expect((fetchMock as any).mock.calls[0]?.[1].headers).not.toHaveProperty('Content-Type')
    expect((fetchMock as any).mock.calls[0]?.[1]).toEqual(expect.objectContaining({ credentials: 'include' }))
  })

  it('surfaces API error details on failed responses', async () => {
    installBrowser()
    Object.defineProperty(globalThis, 'fetch', {
      configurable: true,
      value: vi.fn(async () => ({
        ok: false,
        status: 403,
        json: async () => ({ message: 'Forbidden', error: 'FORBIDDEN' }),
      })),
    })

    const { apiFetch } = await import('./api')
    await expect(apiFetch('/team')).rejects.toMatchObject({ message: 'Forbidden', code: 'FORBIDDEN', statusCode: 403 })
  })

  it('uses explicit server token and disables caching for server requests', async () => {
    Reflect.deleteProperty(globalThis, 'window')
    const fetchMock = vi.fn(async () => ({ ok: true, json: async () => ({ data: { ok: true } }) }))
    Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })

    const { apiFetchServer } = await import('./api')
    await apiFetchServer('/fleet', 'server-token')

    expect(fetchMock).toHaveBeenCalledWith('http://localhost:4000/api/v1/fleet', expect.objectContaining({
      cache: 'no-store',
      headers: expect.objectContaining({ Authorization: 'Bearer server-token' }),
    }))
  })
})
