/**
 * @module utils/idNormalization
 * @description The 8.0 entity-id normalization layer. Every id that enters
 * Brainy is coerced to a valid UUID, because a native engine maps ids to compact
 * integers and requires real UUIDs (`encode()` rejects arbitrary strings).
 *
 * The rules (decided as AR.1 = "normalize"):
 * - **No id supplied** → a fresh time-ordered {@link v7} UUID (sortable by
 *   creation time; friendly to the id↔int mapper and range scans).
 * - **A valid UUID** → used as-is.
 * - **Any other string** (a natural key like `'user-123'`) → a STABLE
 *   {@link v5} UUID derived from it, so the same key always maps to the same id
 *   and the caller can keep using their natural key on every read/relate while
 *   the engine only ever sees a UUID. The original string is preserved so reads
 *   can surface it.
 *
 * Two entry points: {@link coerceNewEntityId} at creation (add/relate), and
 * {@link resolveEntityId} at lookup (get/update/remove/relate endpoints).
 */
import { v5, v7, isUUID } from '../universal/uuid.js'

/** Metadata key under which a caller's original (non-UUID) id is preserved. */
export const ORIGINAL_ID_KEY = '_originalId'

/**
 * @description The result of normalizing an id at creation time.
 */
export interface CoercedId {
  /** The canonical UUID to store and hand to the engine. */
  id: string
  /** The caller's original string, present only when it was normalized (non-UUID). */
  originalId?: string
}

/**
 * @description Normalize an id supplied at CREATION (add/relate). Generates a
 * time-ordered UUID when none is given, passes a real UUID through, and maps any
 * other string to a stable namespaced UUID (recording the original).
 * @param id - The caller-supplied id, or undefined to auto-generate.
 * @returns The canonical UUID and, when normalized, the original string.
 * @example
 * coerceNewEntityId()            // → { id: <uuidv7> }
 * coerceNewEntityId('user-123')  // → { id: <stable uuidv5>, originalId: 'user-123' }
 */
export function coerceNewEntityId(id?: string | null): CoercedId {
  if (id === undefined || id === null || id === '') {
    return { id: v7() }
  }
  if (isUUID(id)) {
    return { id }
  }
  return { id: v5(id), originalId: id }
}

/**
 * @description Normalize an id used for LOOKUP (get/update/remove/relate
 * endpoints) to the same canonical UUID `coerceNewEntityId` produced — so a
 * caller can read/relate by their natural key transparently. A real UUID passes
 * through; any other string maps to its stable namespaced UUID.
 * @param id - The id to resolve.
 * @returns The canonical UUID.
 */
export function resolveEntityId(id: string): string {
  return isUUID(id) ? id : v5(id)
}
