// Block registry (server side) — the single source of truth for what block
// types exist, how their props validate, and how long a rendered block may be
// cached. The admin builder UI, the public renderer, and this server-side
// validation are all driven from a registry entry rather than a switch statement
// scattered across files: adding a block later means adding ONE entry (here on
// the server for schema/cache, and one in client/src/blocks for the React
// renderer/editor), not editing four places.
//
// A registered definition looks like:
//   {
//     type: 'heading',        // stable string id, unique across the registry
//     version: 1,             // prop-schema version; bump when props change so a
//                             // one-time migration can transform older blocks
//     schema: (props) => [],  // returns an array of error strings ([] = valid)
//     sanitize: (props) => props, // optional normalizer run on save AFTER
//                             // validation, e.g. rich_text runs its html through
//                             // the shared allowlist; returns cleaned props
//     cacheTTL: null,         // seconds a rendered instance may be cached;
//                             // null = never cache (static blocks). Dynamic
//                             // Wave 2 blocks set this (e.g. server_status: 10).
//     container: false,       // true only for block types that hold sub-blocks
//     containerSlots: [],     // prop keys holding sub-block arrays, e.g.
//                             // ['left','right'] for two_column
//   }
//
// This module is intentionally empty of block types — it only defines the
// pattern. Wave 1 block definitions register themselves via ./index.js.

// The only keys allowed at the top level of a stored block object. Everything
// block-specific lives inside `props`; nothing else lives at the top level.
// Ordering is the array position, not a stored field — so a reorder is just a
// reorder of the array, and `id` is never derived from position.
const RESERVED_KEYS = Object.freeze(['id', 'type', 'version', 'visible', 'props'])

const registry = new Map()

/**
 * Register a block definition. Throws on a missing type or a duplicate — both
 * are programmer errors surfaced at boot, not runtime input.
 * @param {object} def
 * @returns {object} the normalized, frozen definition
 */
function registerBlock(def) {
  if (!def || typeof def.type !== 'string' || def.type.length === 0) {
    throw new Error('registerBlock: a block definition needs a string `type`')
  }
  if (registry.has(def.type)) {
    throw new Error(`registerBlock: block type already registered: ${def.type}`)
  }
  if (def.schema != null && typeof def.schema !== 'function') {
    throw new Error(`registerBlock: ${def.type}.schema must be a function`)
  }
  if (def.sanitize != null && typeof def.sanitize !== 'function') {
    throw new Error(`registerBlock: ${def.type}.sanitize must be a function`)
  }
  const containerSlots = def.containerSlots || []
  if (def.container && containerSlots.length === 0) {
    throw new Error(`registerBlock: container block ${def.type} needs containerSlots`)
  }
  const entry = Object.freeze({
    type: def.type,
    version: Number.isInteger(def.version) ? def.version : 1,
    schema: def.schema || null,
    sanitize: def.sanitize || null,
    cacheTTL: def.cacheTTL == null ? null : Number(def.cacheTTL),
    container: Boolean(def.container),
    containerSlots: Object.freeze([...containerSlots]),
  })
  registry.set(entry.type, entry)
  return entry
}

/** @returns {object|null} the definition for `type`, or null if unknown. */
function getBlock(type) {
  return registry.get(type) || null
}

/** @returns {boolean} whether `type` is a registered block. */
function hasBlock(type) {
  return registry.has(type)
}

/** @returns {object[]} all registered definitions (registration order). */
function listBlocks() {
  return [...registry.values()]
}

/**
 * Drop every registered block. Test-only — lets a suite register a fixture set
 * and start from a known-empty registry.
 */
function _resetRegistry() {
  registry.clear()
}

module.exports = {
  RESERVED_KEYS,
  registerBlock,
  getBlock,
  hasBlock,
  listBlocks,
  _resetRegistry,
}
