import { describe, expect, it } from 'vitest'
import {
  AuthError,
  RateLimitError,
  TransientError,
  type AIRequest,
} from './index.js'
import {
  classifyAnthropicError,
  extractAnthropicSystem,
  makeAnthropicAdapter,
  mapAnthropicResponse,
  mapContentToAnthropic,
  mapMessagesToAnthropic,
} from './anthropic.js'

describe('anthropic adapter', () => {
  const req: AIRequest = {
    model: 'claude-3-5-sonnet-20241022',
    messages: [
      { role: 'system', content: 'be helpful' },
      {
        role: 'user',
        content: [
          { type: 'text', text: 'look' },
          {
            type: 'image',
            image: { mediaType: 'image/jpeg', data: 'imgdata' },
          },
        ],
      },
    ],
    maxTokens: 256,
  }

  it('extracts system prompt and maps non-system messages', () => {
    expect(extractAnthropicSystem(req.messages)).toBe('be helpful')
    expect(mapMessagesToAnthropic(req.messages)).toEqual([
      {
        role: 'user',
        content: [
          { type: 'text', text: 'look' },
          {
            type: 'image',
            source: { type: 'base64', media_type: 'image/jpeg', data: 'imgdata' },
          },
        ],
      },
    ])
  })

  it('maps image blocks to anthropic wire shape', () => {
    const wire = mapContentToAnthropic(req.messages[1]!.content as never)
    expect(wire).toEqual([
      { type: 'text', text: 'look' },
      {
        type: 'image',
        source: { type: 'base64', media_type: 'image/jpeg', data: 'imgdata' },
      },
    ])
  })

  it('maps response usage without cost via fake fetch', async () => {
    const fakeFetch: typeof fetch = async () =>
      new Response(
        JSON.stringify({
          model: 'claude-3-5-sonnet-20241022',
          content: [{ type: 'text', text: 'done' }],
          stop_reason: 'end_turn',
          usage: { input_tokens: 9, output_tokens: 4 },
        }),
        { status: 200, headers: { 'Content-Type': 'application/json' } },
      )

    const adapter = makeAnthropicAdapter({ apiKey: 'k', fetch: fakeFetch })
    const result = await adapter.call(req)
    expect(result.content).toBe('done')
    expect(result.usage).toEqual({ promptTokens: 9, completionTokens: 4 })
    expect(Object.keys(result.usage)).toEqual(['promptTokens', 'completionTokens'])
  })

  it('classifies provider HTTP errors with retryable flags', () => {
    expect(classifyAnthropicError(429, 'rate', 'anthropic')).toBeInstanceOf(RateLimitError)
    expect(classifyAnthropicError(500, 'boom', 'anthropic')).toBeInstanceOf(TransientError)
    expect(classifyAnthropicError(401, 'bad key', 'anthropic')).toBeInstanceOf(AuthError)
    expect(classifyAnthropicError(401, 'bad key', 'anthropic').retryable).toBe(false)
  })

  it('mapAnthropicResponse omits cost fields', () => {
    const mapped = mapAnthropicResponse(
      {
        model: 'claude',
        content: [{ type: 'text', text: 'x' }],
        stop_reason: 'end_turn',
        usage: { input_tokens: 1, output_tokens: 2 },
      },
      'anthropic',
      'claude',
    )
    expect(mapped.usage).toEqual({ promptTokens: 1, completionTokens: 2 })
    expect('cost' in mapped.usage).toBe(false)
  })
})
