// The arithmetic in codes.js decides whether a printed label scans, and it is
// the part no amount of looking at a screen will verify - a QR that is fine at
// 96 dpi on a monitor can be unreadable at 203 dpi on 0.5in stock.

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

import {
  LABEL_PRESETS, BARCODE_DEFAULTS, qrModuleCount, qrFit, svgDataUri,
} from './codes'

describe('qrModuleCount', () => {
  it('grows with the content', () => {
    const short = qrModuleCount('AP-01')
    const long = qrModuleCount('AP-01/'.repeat(20))
    expect(short).toBeGreaterThan(0)
    expect(long).toBeGreaterThan(short)
  })

  it('grows with error correction, because the redundancy is stored in modules', () => {
    expect(qrModuleCount('WKSTN0042', 'H')).toBeGreaterThanOrEqual(
      qrModuleCount('WKSTN0042', 'L'))
  })

  it('returns 0 rather than throwing on empty content', () => {
    expect(qrModuleCount('')).toBe(0)
    expect(qrModuleCount(null)).toBe(0)
  })
})

describe('qrFit', () => {
  it('calls a code too small when a module gets under two printer dots', () => {
    // A long payload squeezed onto the 1.00 x 0.50in stock: the case the Tech
    // Tools generator exists to warn about before 300 labels are printed.
    const fit = qrFit('https://shopdb.example.net/assets/1234?from=label&sig=abcdef123456', {
      sizeInches: 0.4286, dpi: 203, errorCorrection: 'H',
    })
    expect(fit.tooSmall).toBe(true)
    expect(fit.dotsPerModule).toBeLessThan(2)
  })

  it('reports a whole number of dots per module as even', () => {
    const modules = qrModuleCount('WKSTN0042', 'M')
    // Choose the size that divides exactly at 4 dots per module.
    const fit = qrFit('WKSTN0042', {
      sizeInches: (modules * 4) / 203, dpi: 203, errorCorrection: 'M',
    })
    expect(fit.wholeDots).toBe(4)
    expect(fit.even).toBe(true)
    expect(fit.snapped).toBeCloseTo(fit.snapped, 5)
  })

  it('offers the nearest evenly-dividing size when the chosen one is uneven', () => {
    const fit = qrFit('WKSTN0042', { sizeInches: 0.9, dpi: 203, errorCorrection: 'M' })
    expect(fit.even).toBe(false)
    expect(fit.snapped).toBeLessThanOrEqual(0.9)
    expect(fit.snapped * 203 / fit.modules).toBeCloseTo(fit.wholeDots, 6)
  })

  it('is null when there is nothing to measure', () => {
    expect(qrFit('', { sizeInches: 1 })).toBeNull()
    expect(qrFit('WKSTN0042', {})).toBeNull()
  })
})

describe('shared defaults', () => {
  it('barcodes are CODE128 with no margin and no printed text', () => {
    // The label draws its own caption, and the quiet zone is blank label. Both
    // had drifted per-view before this module existed.
    expect(BARCODE_DEFAULTS.format).toBe('CODE128')
    expect(BARCODE_DEFAULTS.margin).toBe(0)
    expect(BARCODE_DEFAULTS.displayValue).toBe(false)
  })

  it('every label preset is a complete, usable stock definition', () => {
    expect(LABEL_PRESETS.length).toBeGreaterThan(0)
    for (const preset of LABEL_PRESETS) {
      expect(preset.id).toBeTruthy()
      expect(preset.labelwidth).toBeGreaterThan(0)
      expect(preset.labelheight).toBeGreaterThan(0)
      // A code has to fit inside its label with its quiet zone on both sides.
      expect(preset.codesize + 2 * preset.quiet).toBeLessThanOrEqual(preset.labelwidth)
    }
  })
})

describe('svgDataUri', () => {
  it('escapes markup so the URI survives being put in a src attribute', () => {
    const uri = svgDataUri('<svg><rect fill="#000"/></svg>')
    expect(uri.startsWith('data:image/svg+xml;charset=utf-8,')).toBe(true)
    expect(uri).not.toContain('<')
    expect(uri).not.toContain('"')
  })
})
