import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { describe, it, expect, beforeAll, afterEach, afterAll } from 'vitest'
import { listModels, createModel, updateModel, deleteModel } from '@/api/models'

const mockModels = [
  {
    id: 'gpt-4',
    provider_id: 'prov-1',
    model_name: 'gpt-4',
    unified_id: 'prov-1/gpt-4',
    enabled: true,
    created_at: '2025-01-01T00:00:00Z',
  },
  {
    id: 'claude-3',
    provider_id: 'prov-2',
    model_name: 'claude-3',
    unified_id: 'prov-2/claude-3',
    enabled: false,
    created_at: '2025-01-02T00:00:00Z',
  },
]

describe('models API', () => {
  const server = setupServer()

  beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }))
  afterEach(() => server.resetHandlers())
  afterAll(() => server.close())

  describe('listModels', () => {
    it('returns array of Model objects with camelCase keys', async () => {
      server.use(
        http.get('http://localhost:3000/api/models', () => {
          return HttpResponse.json(mockModels)
        })
      )

      const result = await listModels()

      expect(result).toEqual([
        {
          id: 'gpt-4',
          providerId: 'prov-1',
          modelName: 'gpt-4',
          unifiedId: 'prov-1/gpt-4',
          enabled: true,
          createdAt: '2025-01-01T00:00:00Z',
        },
        {
          id: 'claude-3',
          providerId: 'prov-2',
          modelName: 'claude-3',
          unifiedId: 'prov-2/claude-3',
          enabled: false,
          createdAt: '2025-01-02T00:00:00Z',
        },
      ])
    })

    it('appends provider_id query parameter when providerId is given', async () => {
      let receivedUrl: string | null = null

      server.use(
        http.get('http://localhost:3000/api/models', ({ request }) => {
          receivedUrl = request.url
          return HttpResponse.json([mockModels[0]])
        })
      )

      const result = await listModels('prov-1')

      expect(receivedUrl).toContain('provider_id=prov-1')
      expect(result).toHaveLength(1)
      expect(result[0].providerId).toBe('prov-1')
    })
  })

  describe('createModel', () => {
    it('sends POST with correct body and returns model', async () => {
      let receivedMethod: string | null = null
      let receivedBody: Record<string, unknown> | null = null

      server.use(
        http.post('http://localhost:3000/api/models', async ({ request }) => {
          receivedMethod = request.method
          receivedBody = (await request.json()) as Record<string, unknown>
          return HttpResponse.json(mockModels[0])
        })
      )

      const input = {
        providerId: 'prov-1',
        modelName: 'gpt-4',
        enabled: true,
      }

      const result = await createModel(input)

      expect(receivedMethod).toBe('POST')
      expect(receivedBody).toEqual({
        provider_id: 'prov-1',
        model_name: 'gpt-4',
        enabled: true,
      })
      expect(result.id).toBe('gpt-4')
      expect(result.providerId).toBe('prov-1')
      expect(result.modelName).toBe('gpt-4')
      expect(result.unifiedId).toBe('prov-1/gpt-4')
    })
  })

  describe('updateModel', () => {
    it('sends PUT with correct body and returns model', async () => {
      let receivedMethod: string | null = null
      let receivedUrl: string | null = null
      let receivedBody: Record<string, unknown> | null = null

      server.use(
        http.put('http://localhost:3000/api/models/:id', async ({ request }) => {
          receivedMethod = request.method
          receivedUrl = new URL(request.url).pathname
          receivedBody = (await request.json()) as Record<string, unknown>
          return HttpResponse.json({
            ...mockModels[0],
            model_name: 'gpt-4-turbo',
            enabled: false,
          })
        })
      )

      const result = await updateModel('gpt-4', {
        modelName: 'gpt-4-turbo',
        enabled: false,
      })

      expect(receivedMethod).toBe('PUT')
      expect(receivedUrl).toBe('/api/models/gpt-4')
      expect(receivedBody).toEqual({
        model_name: 'gpt-4-turbo',
        enabled: false,
      })
      expect(result.modelName).toBe('gpt-4-turbo')
      expect(result.enabled).toBe(false)
    })
  })

  describe('deleteModel', () => {
    it('sends DELETE and returns void', async () => {
      let receivedMethod: string | null = null
      let receivedUrl: string | null = null

      server.use(
        http.delete('http://localhost:3000/api/models/:id', ({ request }) => {
          receivedMethod = request.method
          receivedUrl = new URL(request.url).pathname
          return new HttpResponse(null, { status: 204 })
        })
      )

      const result = await deleteModel('gpt-4')

      expect(receivedMethod).toBe('DELETE')
      expect(receivedUrl).toBe('/api/models/gpt-4')
      expect(result).toBeUndefined()
    })
  })
})
