export class FulfillmentValidationError extends Error {
  override readonly name = 'FulfillmentValidationError'
  readonly code = 'FULFILLMENT_VALIDATION' as const

  constructor(readonly field: string) {
    super(`fulfillment validation failed: ${field}`)
  }
}

export class VoucherAlreadyRedeemedError extends Error {
  override readonly name = 'VoucherAlreadyRedeemedError'
  readonly code = 'VOUCHER_ALREADY_REDEEMED' as const
  readonly httpStatus = 409 as const

  constructor(readonly voucherId: string) {
    super(`voucher already redeemed: ${voucherId}`)
  }
}

export class VoucherExpiredError extends Error {
  override readonly name = 'VoucherExpiredError'
  readonly code = 'VOUCHER_EXPIRED' as const
  readonly httpStatus = 422 as const

  constructor(readonly voucherId: string) {
    super(`voucher expired: ${voucherId}`)
  }
}

export class VoucherWrongVendorError extends Error {
  override readonly name = 'VoucherWrongVendorError'
  readonly code = 'VOUCHER_WRONG_VENDOR' as const
  readonly httpStatus = 403 as const

  constructor(
    readonly context: { voucherId: string; scanningVendorId: string },
  ) {
    super(
      `voucher wrong vendor: ${context.voucherId} (scanning ${context.scanningVendorId})`,
    )
  }
}

export class LabelPurchaseError extends Error {
  override readonly name = 'LabelPurchaseError'
  readonly code = 'LABEL_PURCHASE' as const
  readonly httpStatus = 502 as const

  constructor(readonly shipmentId: string, readonly detail: string) {
    super(`label purchase failed for shipment ${shipmentId}: ${detail}`)
  }
}

export class UnfulfillableLineError extends Error {
  override readonly name = 'UnfulfillableLineError'
  readonly code = 'UNFULFILLABLE_LINE' as const
  readonly httpStatus = 422 as const

  constructor(
    readonly context: { orderId: string; lineId: string; kind: string },
  ) {
    super(
      `unfulfillable line: order ${context.orderId}, line ${context.lineId}, kind ${context.kind}`,
    )
  }
}

export class DownloadNotFoundError extends Error {
  override readonly name = 'DownloadNotFoundError'
  readonly code = 'DOWNLOAD_NOT_FOUND' as const
  readonly httpStatus = 404 as const

  constructor(readonly grantId: string) {
    super(`download not found: ${grantId}`)
  }
}

export function isFulfillmentValidationError(
  e: unknown,
): e is FulfillmentValidationError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'FulfillmentValidationError' &&
    (e as { code?: unknown }).code === 'FULFILLMENT_VALIDATION'
  )
}

export function isVoucherAlreadyRedeemedError(
  e: unknown,
): e is VoucherAlreadyRedeemedError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'VoucherAlreadyRedeemedError' &&
    (e as { code?: unknown }).code === 'VOUCHER_ALREADY_REDEEMED'
  )
}

export function isVoucherExpiredError(e: unknown): e is VoucherExpiredError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'VoucherExpiredError' &&
    (e as { code?: unknown }).code === 'VOUCHER_EXPIRED'
  )
}

export function isVoucherWrongVendorError(
  e: unknown,
): e is VoucherWrongVendorError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'VoucherWrongVendorError' &&
    (e as { code?: unknown }).code === 'VOUCHER_WRONG_VENDOR'
  )
}

export function isLabelPurchaseError(e: unknown): e is LabelPurchaseError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'LabelPurchaseError' &&
    (e as { code?: unknown }).code === 'LABEL_PURCHASE'
  )
}

export function isUnfulfillableLineError(
  e: unknown,
): e is UnfulfillableLineError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'UnfulfillableLineError' &&
    (e as { code?: unknown }).code === 'UNFULFILLABLE_LINE'
  )
}

export function isDownloadNotFoundError(
  e: unknown,
): e is DownloadNotFoundError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'DownloadNotFoundError' &&
    (e as { code?: unknown }).code === 'DOWNLOAD_NOT_FOUND'
  )
}
