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

const { mockQueryRaw } = vi.hoisted(() => ({ mockQueryRaw: vi.fn() }))

vi.mock('@/lib/prisma', () => ({
  prisma: { $queryRaw: mockQueryRaw },
}))

import { getTokenStats } from '@/lib/insights/token-stats'

beforeEach(() => {
  vi.clearAllMocks()
})

describe('getTokenStats', () => {
  it('returns empty result for empty sprintId', async () => {
    const result = await getTokenStats('user-1', '')

    expect(result.kpi.totalTokens).toBe(0)
    expect(result.kpi.totalCostUsd).toBe(0)
    expect(result.kpi.avgCostPerJob).toBe(0)
    expect(result.kpi.jobCount).toBe(0)
    expect(result.jobs).toHaveLength(0)
    expect(mockQueryRaw).not.toHaveBeenCalled()
  })

  it('maps kpi rows correctly', async () => {
    const kpiRows = [{ total_tokens: BigInt(10000), total_cost: 0.15, avg_cost: 0.05, job_count: BigInt(3) }]
    const jobRows: unknown[] = []
    mockQueryRaw.mockResolvedValueOnce(kpiRows).mockResolvedValueOnce(jobRows)

    const result = await getTokenStats('user-1', 'sprint-1')

    expect(result.kpi.totalTokens).toBe(10000)
    expect(result.kpi.totalCostUsd).toBe(0.15)
    expect(result.kpi.avgCostPerJob).toBe(0.05)
    expect(result.kpi.jobCount).toBe(3)
  })

  it('maps job rows and handles null token data', async () => {
    const kpiRows = [{ total_tokens: BigInt(0), total_cost: null, avg_cost: null, job_count: BigInt(0) }]
    const jobRows = [
      {
        job_id: 'job-1',
        task_title: 'My Task',
        idea_code: null,
        model_id: 'claude-sonnet-4-6',
        input_tokens: null,
        output_tokens: null,
        cache_read_tokens: null,
        cache_write_tokens: null,
        cost_usd: null,
        duration_seconds: 42,
      },
    ]
    mockQueryRaw.mockResolvedValueOnce(kpiRows).mockResolvedValueOnce(jobRows)

    const result = await getTokenStats('user-1', 'sprint-1')

    expect(result.jobs).toHaveLength(1)
    const job = result.jobs[0]
    expect(job.jobId).toBe('job-1')
    expect(job.taskTitle).toBe('My Task')
    expect(job.costUsd).toBeNull()
    expect(job.durationSeconds).toBe(42)
  })
})
