const hasTag = (e: unknown, tag: string): e is { _tag: string } =>
  typeof e === 'object' && e !== null && (e as { _tag?: unknown })._tag === tag

export class MenuAuthorizationError extends Error {
  readonly _tag = 'MenuAuthorizationError' as const
  override readonly name = 'MenuAuthorizationError'
  constructor(readonly actorId: string, readonly action: string) {
    super(`actor ${actorId} cannot ${action}`)
  }
}

export class MenuNotFoundError extends Error {
  readonly _tag = 'MenuNotFoundError' as const
  override readonly name = 'MenuNotFoundError'
  constructor(readonly kind: 'menu' | 'item', readonly id: string) {
    super(`${kind} not found: ${id}`)
  }
}

export class MenuValidationError extends Error {
  readonly _tag = 'MenuValidationError' as const
  override readonly name = 'MenuValidationError'
  constructor(readonly field: string, readonly detail: string) {
    super(`menu invalid: ${field} — ${detail}`)
  }
}

export class MenuDepthExceededError extends Error {
  readonly _tag = 'MenuDepthExceededError' as const
  override readonly name = 'MenuDepthExceededError'
  constructor(readonly maxDepth: number) {
    super(`menu depth exceeds max ${maxDepth}`)
  }
}

export class MenuLimitExceededError extends Error {
  readonly _tag = 'MenuLimitExceededError' as const
  override readonly name = 'MenuLimitExceededError'
  constructor(readonly maxItems: number) {
    super(`menu item count exceeds max ${maxItems}`)
  }
}

export class MenuCycleError extends Error {
  readonly _tag = 'MenuCycleError' as const
  override readonly name = 'MenuCycleError'
  constructor(readonly itemId: string) {
    super(`menu cycle detected at item ${itemId}`)
  }
}

export class MenuUrlSchemeError extends Error {
  readonly _tag = 'MenuUrlSchemeError' as const
  override readonly name = 'MenuUrlSchemeError'
  constructor(readonly url: string) {
    super(`menu url scheme not allowed: ${url}`)
  }
}

export const isMenuAuthorizationError = (e: unknown): e is MenuAuthorizationError =>
  hasTag(e, 'MenuAuthorizationError') && 'actorId' in (e as object) && 'action' in (e as object)

export const isMenuNotFoundError = (e: unknown): e is MenuNotFoundError =>
  hasTag(e, 'MenuNotFoundError') && 'kind' in (e as object) && 'id' in (e as object)

export const isMenuValidationError = (e: unknown): e is MenuValidationError =>
  hasTag(e, 'MenuValidationError') && 'field' in (e as object) && 'detail' in (e as object)

export const isMenuDepthExceededError = (e: unknown): e is MenuDepthExceededError =>
  hasTag(e, 'MenuDepthExceededError') && 'maxDepth' in (e as object)

export const isMenuLimitExceededError = (e: unknown): e is MenuLimitExceededError =>
  hasTag(e, 'MenuLimitExceededError') && 'maxItems' in (e as object)

export const isMenuCycleError = (e: unknown): e is MenuCycleError =>
  hasTag(e, 'MenuCycleError') && 'itemId' in (e as object)

export const isMenuUrlSchemeError = (e: unknown): e is MenuUrlSchemeError =>
  hasTag(e, 'MenuUrlSchemeError') && 'url' in (e as object)
