import { describe, expect, it } from 'vitest'
import {
  EntitlementDeniedError,
  IdempotencyConflictError,
  MeteringStorageError,
  MeteringValidationError,
  QuotaExhaustedError,
  ReservationConflictError,
  ReservationExpiredError,
  ReservationNotFoundError,
  isEntitlementDeniedError,
  isIdempotencyConflictError,
  isMeteringError,
  isMeteringStorageError,
  isMeteringValidationError,
  isQuotaExhaustedError,
  isReservationConflictError,
  isReservationExpiredError,
  isReservationNotFoundError,
} from './errors.js'

const subject = { tenantId: 'tenant-1', account: 'account-1' }

describe('metering error guards', () => {
  const cases: Array<{
    name: string
    guard: (error: unknown) => boolean
    error: { name: string; code: string }
  }> = [
    {
      name: 'isEntitlementDeniedError',
      guard: isEntitlementDeniedError,
      error: new EntitlementDeniedError({ subject, meter: 'api.calls', at: new Date('2026-01-01T00:00:00Z') }),
    },
    {
      name: 'isQuotaExhaustedError',
      guard: isQuotaExhaustedError,
      error: new QuotaExhaustedError({ requested: 5n, includedRemaining: 2n, capacityRemaining: 2n }),
    },
    {
      name: 'isReservationConflictError',
      guard: isReservationConflictError,
      error: new ReservationConflictError({ reservationId: 'reservation-1', detail: 'already committed' }),
    },
    {
      name: 'isReservationNotFoundError',
      guard: isReservationNotFoundError,
      error: new ReservationNotFoundError('reservation-2'),
    },
    {
      name: 'isReservationExpiredError',
      guard: isReservationExpiredError,
      error: new ReservationExpiredError('reservation-3'),
    },
    {
      name: 'isIdempotencyConflictError',
      guard: isIdempotencyConflictError,
      error: new IdempotencyConflictError({ tenantId: 'tenant-1', idempotencyKey: 'key-1' }),
    },
    {
      name: 'isMeteringValidationError',
      guard: isMeteringValidationError,
      error: new MeteringValidationError('units', 'must be positive'),
    },
    {
      name: 'isMeteringStorageError',
      guard: isMeteringStorageError,
      error: new MeteringStorageError('reserve', new Error('database unavailable')),
    },
  ]

  it.each(cases)('$name accepts its own and duplicated error shape only', ({ guard, error }) => {
    expect(guard(error)).toBe(true)
    expect(guard({ name: error.name, code: error.code })).toBe(true)
    expect(guard({ name: error.name })).toBe(false)
    expect(guard({ code: error.code })).toBe(false)
    expect(guard(new Error('foreign'))).toBe(false)
    expect(guard(null)).toBe(false)
    expect(guard(undefined)).toBe(false)
  })

  it('exposes quota capacity context and identifies the union structurally', () => {
    const error = new QuotaExhaustedError({ requested: 5n, includedRemaining: 2n, capacityRemaining: 2n })
    expect(error.requested).toBe(5n)
    expect(error.includedRemaining).toBe(2n)
    expect(error.capacityRemaining).toBe(2n)
    expect(isMeteringError(error)).toBe(true)
    expect(isMeteringError({ name: error.name, code: error.code })).toBe(true)
    expect(isMeteringError({})).toBe(false)
    expect(isMeteringError(new Error('foreign'))).toBe(false)
  })
})
