import { describe, expect, it } from 'vitest'
import { reviveCart } from './revive.js'
import { isCartWireError } from './errors.js'

const line = (over: Record<string, unknown> = {}) => ({
  lineId: 'l1',
  variantId: 'v1',
  qty: 2,
  price: { amount: '1500', currency: 'USD', priceMode: 'exclusive' },
  vendorId: null,
  ...over,
})
const cart = (over: Record<string, unknown> = {}) => ({
  id: 'c1',
  currency: 'USD',
  lines: [line()],
  subtotal: '3000',
  ...over,
})

describe('reviveCart', () => {
  it('string amounts → bigint (subtotal + line price.amount)', () => {
    const c = reviveCart(cart())
    expect(c.subtotal).toBe(3000n)
    expect(c.lines[0]!.price.amount).toBe(1500n)
    expect(typeof c.lines[0]!.price.amount).toBe('bigint')
  })

  it('number amounts within safe range → bigint', () => {
    const c = reviveCart(cart({ subtotal: 3000, lines: [line({ price: { amount: 1500, currency: 'USD', priceMode: 'inclusive' } })] }))
    expect(c.subtotal).toBe(3000n)
    expect(c.lines[0]!.price.amount).toBe(1500n)
    expect(c.lines[0]!.price.priceMode).toBe('inclusive')
  })

  it('out-of-safe-range numeric amount → CartWireError (must be string-encoded)', () => {
    const bad = cart({ subtotal: 2 ** 53 })
    expect(() => reviveCart(bad)).toThrowError()
    try { reviveCart(bad) } catch (e) { expect(isCartWireError(e)).toBe(true) }
  })

  it('non-integer amount string → CartWireError (no silent 0n / hex / float)', () => {
    for (const amount of ['', '  ', '0x10', '1.5', 'abc']) {
      expect(() => reviveCart(cart({ subtotal: amount }))).toThrowError()
      try { reviveCart(cart({ subtotal: amount })) } catch (e) { expect(isCartWireError(e)).toBe(true) }
    }
  })

  it('qty zero / negative / fractional / ≥2^53 → CartWireError', () => {
    for (const qty of [0, -1, 1.5, 2 ** 53]) {
      expect(() => reviveCart(cart({ lines: [line({ qty })] }))).toThrowError()
      try { reviveCart(cart({ lines: [line({ qty })] })) } catch (e) { expect(isCartWireError(e)).toBe(true) }
    }
  })

  it('bad priceMode → CartWireError', () => {
    expect(() => reviveCart(cart({ lines: [line({ price: { amount: '1', currency: 'USD', priceMode: 'nope' } })] }))).toThrowError()
  })

  it('vendorId null preserved; string vendorId preserved', () => {
    expect(reviveCart(cart()).lines[0]!.vendorId).toBeNull()
    expect(reviveCart(cart({ lines: [line({ vendorId: 'seller-9' })] })).lines[0]!.vendorId).toBe('seller-9')
  })

  it('non-object / non-array lines → CartWireError', () => {
    expect(() => reviveCart(null)).toThrowError()
    expect(() => reviveCart(cart({ lines: 'x' }))).toThrowError()
  })
})
describe('wire-trust hardening (uniform -react sweep)', () => {
  it('amount string over 21 digits → CartWireError (bounded BigInt parse, no DoS)', () => {
    for (const long of ['9'.repeat(22), '-' + '9'.repeat(22), '1'.repeat(1_000_000)]) {
      expect(() => reviveCart(cart({ subtotal: long }))).toThrowError()
      try { reviveCart(cart({ subtotal: long })) } catch (e) { expect(isCartWireError(e)).toBe(true) }
    }
  })

  it('21-digit amount string (boundary) still parses exactly', () => {
    const max = '9'.repeat(21)
    const c = reviveCart(cart({ subtotal: max }))
    expect(c.subtotal).toBe(BigInt(max))
  })
})
