import { describe, it, expect, beforeEach, vi } from 'vitest'
import { buildCsvDownloadItem } from './csv-item'

let csvText = ''
let revokeSpy: ReturnType<typeof vi.spyOn>

beforeEach(() => {
  csvText = ''
  vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:csv')
  revokeSpy = vi
    .spyOn(URL, 'revokeObjectURL')
    .mockImplementation(() => undefined)
  const RealBlob = global.Blob
  vi.stubGlobal(
    'Blob',
    class extends RealBlob {
      constructor(parts: BlobPart[], opts?: BlobPropertyBag) {
        csvText = typeof parts[0] === 'string' ? parts[0] : ''
        super(parts, opts)
      }
    },
  )
})

describe('buildCsvDownloadItem', () => {
  it('returns an item with the canonical CSV shape (id, label, icon)', () => {
    const item = buildCsvDownloadItem({ filename: 'demo', getRows: () => [] })
    expect(item.id).toBe('csv')
    expect(item.label).toBe('CSV')
    expect(item.icon).toBeTruthy()
  })

  it('honours a label override', () => {
    const item = buildCsvDownloadItem({
      filename: 'demo',
      getRows: () => [],
      label: 'Export data',
    })
    expect(item.label).toBe('Export data')
  })

  it('getRows path serialises rows via toCsvString and returns a .csv handle', async () => {
    const item = buildCsvDownloadItem({
      filename: 'sales',
      getRows: () => [
        ['name', 'value'],
        ['a', 1],
      ],
    })
    const handle = await item.resolve()
    expect(handle.url).toBe('blob:csv')
    expect(handle.filename).toBe('sales.csv')
    expect(csvText).toBe('name,value\na,1')
    handle.revoke?.()
    expect(revokeSpy).toHaveBeenCalledWith('blob:csv')
  })

  it('getRows path applies the formula-injection guard', async () => {
    const item = buildCsvDownloadItem({
      filename: 'sales',
      getRows: () => [['=SUM(A1)']],
    })
    await item.resolve()
    expect(csvText).toBe("'=SUM(A1)")
  })

  it('getCsv path passes the pre-built CSV through verbatim', async () => {
    const item = buildCsvDownloadItem({
      filename: 'table',
      getCsv: () => 'col\n=raw',
    })
    const handle = await item.resolve()
    expect(handle.filename).toBe('table.csv')
    // getCsv is an escape hatch — content is used as-is, no extra escaping.
    expect(csvText).toBe('col\n=raw')
  })
})
