import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { mkdtemp, rm } from 'fs/promises'
import { tmpdir } from 'os'
import { join } from 'path'
import { fetchVercelGatewayReport, vercelGateway } from '../../src/providers/vercel-gateway.js'
import { parseAllSessions, clearSessionCache } from '../../src/parser.js'

describe('vercel-gateway provider', () => {
  const originalFetch = globalThis.fetch
  const originalKey = process.env.AI_GATEWAY_API_KEY

  beforeEach(() => {
    process.env.AI_GATEWAY_API_KEY = 'test-key'
  })

  afterEach(() => {
    globalThis.fetch = originalFetch
    if (originalKey === undefined) delete process.env.AI_GATEWAY_API_KEY
    else process.env.AI_GATEWAY_API_KEY = originalKey
    vi.restoreAllMocks()
  })

  it('discovers a session when API key is set', async () => {
    const sessions = await vercelGateway.discoverSessions()
    expect(sessions).toHaveLength(1)
    expect(sessions[0]?.provider).toBe('vercel-gateway')
  })

  it('returns empty discovery without API key', async () => {
    delete process.env.AI_GATEWAY_API_KEY
    delete process.env.VERCEL_OIDC_TOKEN
    const sessions = await vercelGateway.discoverSessions()
    expect(sessions).toEqual([])
  })

  it('maps report rows to parsed calls', async () => {
    globalThis.fetch = vi.fn(async () => ({
      ok: true,
      json: async () => ({
        results: [{
          day: '2026-06-01',
          model: 'anthropic/claude-sonnet-4.6',
          total_cost: 1.25,
          input_tokens: 1000,
          output_tokens: 200,
          request_count: 3,
        }],
      }),
    })) as typeof fetch

    const range = {
      start: new Date('2026-06-01T00:00:00.000Z'),
      end: new Date('2026-06-02T23:59:59.999Z'),
    }
    const rows = await fetchVercelGatewayReport(range)
    expect(rows).toHaveLength(1)

    const source = { path: 'vercel-ai-gateway:report', project: 'Vercel AI Gateway', provider: 'vercel-gateway' }
    const seen = new Set<string>()
    const calls = []
    for await (const call of vercelGateway.createSessionParser(source, seen, range).parse()) {
      calls.push(call)
    }
    expect(calls).toHaveLength(1)
    expect(calls[0]?.costUSD).toBe(1.25)
    expect(calls[0]?.model).toBe('anthropic/claude-sonnet-4.6')
  })
})

describe('vercel-gateway end-to-end (parseAllSessions network path)', () => {
  const originalFetch = globalThis.fetch
  const originalKey = process.env.AI_GATEWAY_API_KEY
  const originalCacheDir = process.env.CODEBURN_CACHE_DIR
  let cacheDir: string

  beforeEach(async () => {
    cacheDir = await mkdtemp(join(tmpdir(), 'cb-vercel-cache-'))
    process.env.CODEBURN_CACHE_DIR = cacheDir
    process.env.AI_GATEWAY_API_KEY = 'test-key'
    clearSessionCache()
  })

  afterEach(async () => {
    globalThis.fetch = originalFetch
    if (originalKey === undefined) delete process.env.AI_GATEWAY_API_KEY
    else process.env.AI_GATEWAY_API_KEY = originalKey
    if (originalCacheDir === undefined) delete process.env.CODEBURN_CACHE_DIR
    else process.env.CODEBURN_CACHE_DIR = originalCacheDir
    clearSessionCache()
    vi.restoreAllMocks()
    await rm(cacheDir, { recursive: true, force: true })
  })

  // Regression: the synthetic source path `vercel-ai-gateway:report` has no file
  // on disk, so it was dropped by the fingerprintFile gate in parseProviderSources
  // and the provider always reported $0. Network providers must survive that gate
  // and contribute their fetched cost through the real aggregation pipeline.
  it('network source survives the fingerprint gate and contributes cost', async () => {
    globalThis.fetch = vi.fn(async () => ({
      ok: true,
      json: async () => ({
        results: [
          { day: '2026-06-01', model: 'openai/gpt-4o', total_cost: 12.34, input_tokens: 1000, output_tokens: 500, request_count: 3 },
        ],
      }),
    })) as typeof fetch

    const range = {
      start: new Date('2026-05-01T00:00:00.000Z'),
      end: new Date('2026-06-09T23:59:59.999Z'),
    }
    const projects = await parseAllSessions(range, 'vercel-gateway')
    const total = projects.reduce((sum, p) => sum + p.totalCostUSD, 0)

    expect(total).toBeCloseTo(12.34, 2)
  })
})
