import { describe, it, expect } from 'vitest'
import {
  UploadRejectedError,
  isUploadRejectedError,
  UploadInFlightError,
  isUploadInFlightError,
} from './errors.js'

describe('UploadRejectedError', () => {
  it('carries a reason + status and a structural tag', () => {
    const e = new UploadRejectedError('file_too_large', 413)
    expect(e.reason).toBe('file_too_large')
    expect(e.status).toBe(413)
    expect(e.isUploadRejectedError).toBe(true)
    expect(isUploadRejectedError(e)).toBe(true)
  })

  it('guard is structural — true for a plain tagged object (deduped-copy safe), not instanceof-bound', () => {
    expect(isUploadRejectedError({ isUploadRejectedError: true, reason: 'x', status: 400 })).toBe(true)
  })

  it('guard rejects non-matches incl. name-spoof and non-objects', () => {
    expect(isUploadRejectedError(new Error('x'))).toBe(false)
    expect(isUploadRejectedError({ name: 'UploadRejectedError' })).toBe(false) // name-only spoof must NOT pass
    expect(isUploadRejectedError(null)).toBe(false)
    expect(isUploadRejectedError(undefined)).toBe(false)
    expect(isUploadRejectedError('UploadRejectedError')).toBe(false)
    expect(isUploadRejectedError(413)).toBe(false)
    expect(isUploadRejectedError({})).toBe(false)
  })
})

describe('UploadInFlightError', () => {
  it('carries a structural tag and an "in progress" message', () => {
    const e = new UploadInFlightError()
    expect(e.isUploadInFlightError).toBe(true)
    expect(e.name).toBe('UploadInFlightError')
    expect(e.message).toMatch(/in progress/i)
    expect(isUploadInFlightError(e)).toBe(true)
  })

  it('guard is structural — true for a plain tagged object (deduped-copy safe), not instanceof-bound', () => {
    expect(isUploadInFlightError({ isUploadInFlightError: true })).toBe(true)
  })

  it('guard rejects non-matches incl. name-spoof, non-objects, and a sibling error', () => {
    expect(isUploadInFlightError(new Error('x'))).toBe(false)
    expect(isUploadInFlightError({ name: 'UploadInFlightError' })).toBe(false) // name-only spoof must NOT pass
    expect(isUploadInFlightError(null)).toBe(false)
    expect(isUploadInFlightError(undefined)).toBe(false)
    expect(isUploadInFlightError('UploadInFlightError')).toBe(false)
    expect(isUploadInFlightError({})).toBe(false)
    // distinct discriminant: a host-side rejection must NOT match the re-entrancy guard
    expect(isUploadInFlightError(new UploadRejectedError('file_too_large', 413))).toBe(false)
  })
})
