export class OrderValidationError extends Error {
  override readonly name = 'OrderValidationError'
  readonly code = 'ORDER_VALIDATION' as const
  readonly httpStatus = 400 as const

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

export type OrderIntegrityDetail = 'lines' | 'total' | 'splits'

export class OrderIntegrityError extends Error {
  override readonly name = 'OrderIntegrityError'
  readonly code = 'ORDER_INTEGRITY' as const

  constructor(readonly detail: OrderIntegrityDetail) {
    super(`order integrity failed: ${detail}`)
  }
}

export class OrderNotFoundError extends Error {
  override readonly name = 'OrderNotFoundError'
  readonly code = 'ORDER_NOT_FOUND' as const
  readonly httpStatus = 404 as const

  constructor(readonly orderId: string) {
    super(`order not found: ${orderId}`)
  }
}

export class OrderNotChargeableError extends Error {
  override readonly name = 'OrderNotChargeableError'
  readonly code = 'ORDER_NOT_CHARGEABLE' as const
  readonly httpStatus = 409 as const

  constructor(readonly orderId: string) {
    super(`order not chargeable: ${orderId}`)
  }
}

export class OrderIdempotencyConflictError extends Error {
  override readonly name = 'OrderIdempotencyConflictError'
  readonly code = 'ORDER_IDEMPOTENCY_CONFLICT' as const
  readonly httpStatus = 409 as const

  constructor(readonly context: { idempotencyKey: string }) {
    super(`order idempotency conflict: ${context.idempotencyKey}`)
  }
}

export class OrderChargeConflictError extends Error {
  override readonly name = 'OrderChargeConflictError'
  readonly code = 'ORDER_CHARGE_CONFLICT' as const
  readonly httpStatus = 409 as const

  constructor(
    readonly context: { orderId: string; existingChargeRef: string; attemptedChargeRef: string },
  ) {
    super(
      `order charge conflict: ${context.orderId} (existing ${context.existingChargeRef}, attempted ${context.attemptedChargeRef})`,
    )
  }
}

export class RefundExceedsPaidError extends Error {
  override readonly name = 'RefundExceedsPaidError'
  readonly code = 'REFUND_EXCEEDS_PAID' as const
  readonly httpStatus = 422 as const

  constructor(
    readonly context: {
      orderId: string
      requested: bigint
      alreadyRefunded: bigint
      paid: bigint
    },
  ) {
    super(
      `refund exceeds paid: order ${context.orderId}, requested ${context.requested}, already ${context.alreadyRefunded}, paid ${context.paid}`,
    )
  }
}

export type ImmutableField = 'orderId' | 'currency' | 'priceMode'

export class ImmutableFieldError extends Error {
  override readonly name = 'ImmutableFieldError'
  readonly code = 'IMMUTABLE_FIELD' as const

  constructor(readonly field: ImmutableField) {
    super(`field cannot be changed after create: ${field}`)
  }
}

export class OrderMigrateError extends Error {
  override readonly name = 'OrderMigrateError'
  readonly code = 'ORDER_MIGRATE' as const

  constructor(readonly detail: string) {
    super(`order migrate: ${detail}`)
  }
}

export class ReturnTransitionError extends Error {
  override readonly name = 'ReturnTransitionError'
  readonly code = 'RETURN_TRANSITION' as const

  constructor(
    readonly context: { from: string; command: string },
  ) {
    super(`invalid return transition: ${context.command} from ${context.from}`)
  }
}

export function isOrderValidationError(e: unknown): e is OrderValidationError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderValidationError' &&
    (e as { code?: unknown }).code === 'ORDER_VALIDATION'
  )
}

export function isOrderIntegrityError(e: unknown): e is OrderIntegrityError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderIntegrityError' &&
    (e as { code?: unknown }).code === 'ORDER_INTEGRITY'
  )
}

export function isOrderNotFoundError(e: unknown): e is OrderNotFoundError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderNotFoundError' &&
    (e as { code?: unknown }).code === 'ORDER_NOT_FOUND'
  )
}

export function isOrderNotChargeableError(e: unknown): e is OrderNotChargeableError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderNotChargeableError' &&
    (e as { code?: unknown }).code === 'ORDER_NOT_CHARGEABLE'
  )
}

export function isOrderIdempotencyConflictError(e: unknown): e is OrderIdempotencyConflictError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderIdempotencyConflictError' &&
    (e as { code?: unknown }).code === 'ORDER_IDEMPOTENCY_CONFLICT'
  )
}

export function isOrderChargeConflictError(e: unknown): e is OrderChargeConflictError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderChargeConflictError' &&
    (e as { code?: unknown }).code === 'ORDER_CHARGE_CONFLICT'
  )
}

export function isRefundExceedsPaidError(e: unknown): e is RefundExceedsPaidError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'RefundExceedsPaidError' &&
    (e as { code?: unknown }).code === 'REFUND_EXCEEDS_PAID'
  )
}

export function isImmutableFieldError(e: unknown): e is ImmutableFieldError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'ImmutableFieldError' &&
    (e as { code?: unknown }).code === 'IMMUTABLE_FIELD'
  )
}

export function isOrderMigrateError(e: unknown): e is OrderMigrateError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'OrderMigrateError' &&
    (e as { code?: unknown }).code === 'ORDER_MIGRATE'
  )
}

export function isReturnTransitionError(e: unknown): e is ReturnTransitionError {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as { name?: unknown }).name === 'ReturnTransitionError' &&
    (e as { code?: unknown }).code === 'RETURN_TRANSITION'
  )
}
