import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import * as api from '@/api/index.js'

describe('API request helper', () => {
  beforeEach(() => {
    vi.stubGlobal('fetch', vi.fn())
  })

  afterEach(() => {
    vi.unstubAllGlobals()
  })

  it('getProfiles calls GET /agents/profiles', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 200,
      json: async () => [{ id: 'secretary' }],
    })
    const result = await api.getProfiles()
    expect(global.fetch).toHaveBeenCalledOnce()
    const [url, opts] = global.fetch.mock.calls[0]
    expect(url).toContain('/agents/profiles')
    expect(opts.method).toBe('GET')
    expect(result).toEqual([{ id: 'secretary' }])
  })

  it('createSession calls POST /sessions with body', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 201,
      json: async () => ({ session_id: 's1' }),
    })
    const result = await api.createSession('developer')
    const [url, opts] = global.fetch.mock.calls[0]
    expect(url).toContain('/sessions')
    expect(opts.method).toBe('POST')
    expect(opts.headers['Content-Type']).toBe('application/json')
    expect(JSON.parse(opts.body)).toEqual({ profile_id: 'developer' })
    expect(result).toEqual({ session_id: 's1' })
  })

  it('deleteSession calls DELETE', async () => {
    global.fetch.mockResolvedValue({ ok: true, status: 204 })
    await api.deleteSession('s1')
    const [url, opts] = global.fetch.mock.calls[0]
    expect(opts.method).toBe('DELETE')
    expect(url).toContain('/sessions/s1')
  })

  it('204 response returns null', async () => {
    global.fetch.mockResolvedValue({ ok: true, status: 204 })
    const result = await api.deleteSession('s1')
    expect(result).toBeNull()
  })

  it('4xx throws with status text', async () => {
    global.fetch.mockResolvedValue({
      ok: false,
      status: 404,
      text: async () => 'Not found',
    })
    await expect(api.getSession('bad')).rejects.toThrow('404')
  })

  it('getSessions calls GET /sessions', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 200,
      json: async () => ({ items: [] }),
    })
    await api.getSessions()
    const [url] = global.fetch.mock.calls[0]
    expect(url).toContain('/sessions?limit=30&offset=0')
  })

  it('getSessions sends pagination params', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 200,
      json: async () => ({ items: [] }),
    })
    await api.getSessions({ limit: 30, offset: 60 })
    const [url] = global.fetch.mock.calls[0]
    expect(url).toContain('/sessions?limit=30&offset=60')
  })

  it('getSessions sends profile filter', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 200,
      json: async () => ({ items: [] }),
    })
    await api.getSessions({ limit: 10, offset: 0, profileId: 'developer' })
    const [url] = global.fetch.mock.calls[0]
    expect(url).toContain('/sessions?limit=10&offset=0&profile_id=developer')
  })

  it('pinSession calls PATCH with pinned', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 200,
      json: async () => ({ pinned: true }),
    })
    await api.pinSession('s1', true)
    const [url, opts] = global.fetch.mock.calls[0]
    expect(url).toContain('/sessions/s1/pin')
    expect(opts.method).toBe('PATCH')
    expect(JSON.parse(opts.body)).toEqual({ pinned: true })
  })

  it('uploadFile sends FormData', async () => {
    global.fetch.mockResolvedValue({
      ok: true,
      status: 201,
      json: async () => ({ name: 'file.txt' }),
    })
    const file = new File(['content'], 'file.txt', { type: 'text/plain' })
    await api.uploadFile('s1', file)
    const [url, opts] = global.fetch.mock.calls[0]
    expect(url).toContain('/sessions/s1/files')
    expect(opts.method).toBe('POST')
    expect(opts.body instanceof FormData).toBe(true)
  })
})
