/**
 * Snippet placement helpers, shared by the v1↔v2 adapters and the builder.
 *
 * A *snippet* is a reusable, named config fragment (a card or an element)
 * referenced by key instead of duplicated. The definitions live elsewhere
 * (in the builder schema's `snippets` map / the host's
 * `version.settings.snippets`); these helpers concern only the *placement* of
 * a snippet inside a form's child arrays.
 *
 * Wire formats:
 *   - v1: a node `{ type: "snippet", key: "<key>", ...overrides }` placed
 *     anywhere a card or element/field can go. Extra keys (anything except
 *     `type`/`key`) shallow-merge over the stored fragment per use.
 *   - v2: a bare token string in a container's `elementList` / `cardList` /
 *     `tabList` (no backing entry in the `cards` / `elements` maps — the
 *     `snippet#` prefix is the signal):
 *       • `snippet#<key>`                — placement with no per-use overrides.
 *       • `snippet#<key>:<instanceId>`   — placement WITH overrides; the
 *         override values live in `schema.snippetOverrides[instanceId]`.
 *     The instanceId is unique per placement, so the same key reused with
 *     different overrides never collides.
 */

/** Prefix marking a child-array id as a snippet placement. */
export const SNIPPET_PREFIX = "snippet#"

/** Separator between the snippet key and a per-placement instance id. */
export const SNIPPET_INSTANCE_SEP = ":"

/** A v1 snippet reference node. */
export interface V1SnippetNode {
  type: "snippet"
  key: string
  [extra: string]: unknown
}

/** Parsed parts of a `snippet#<key>[:<instanceId>]` token. */
export interface ParsedSnippetToken {
  key: string
  /** Present only when the placement carries per-use overrides. */
  instanceId?: string
}

/** True when an id string is a snippet placement token. */
export function isSnippetToken(id: unknown): id is string {
  return typeof id === "string" && id.startsWith(SNIPPET_PREFIX)
}

/**
 * Build a placement token. With `instanceId` the token is
 * `snippet#<key>:<instanceId>` (it has per-use overrides); without it the
 * token is the bare `snippet#<key>`.
 */
export function snippetToken(key: string, instanceId?: string): string {
  return instanceId
    ? `${SNIPPET_PREFIX}${key}${SNIPPET_INSTANCE_SEP}${instanceId}`
    : `${SNIPPET_PREFIX}${key}`
}

/** Split a placement token into its `{ key, instanceId? }` parts. */
export function parseSnippetToken(id: string): ParsedSnippetToken {
  const body = id.startsWith(SNIPPET_PREFIX)
    ? id.slice(SNIPPET_PREFIX.length)
    : id
  const sep = body.indexOf(SNIPPET_INSTANCE_SEP)
  if (sep === -1) {
    return { key: body }
  }
  return { key: body.slice(0, sep), instanceId: body.slice(sep + 1) }
}

/** The snippet key part of a placement token. */
export function snippetKeyFromToken(id: string): string {
  return parseSnippetToken(id).key
}

/** The per-placement instance id of a token (undefined when none). */
export function snippetInstanceFromToken(id: string): string | undefined {
  return parseSnippetToken(id).instanceId
}

/** True when a v1 node is a snippet reference (`{ type: "snippet", key }`). */
export function isSnippetNode(node: unknown): node is V1SnippetNode {
  return (
    typeof node === "object" &&
    node !== null &&
    (node as Record<string, unknown>).type === "snippet" &&
    typeof (node as Record<string, unknown>).key === "string"
  )
}

/**
 * The override values of a v1 snippet node — everything except the structural
 * keys `type`, `key`, and `id` (the `id` is the placement instance id, carried
 * by the token, not an override value).
 */
export function snippetOverridesOf(
  node: V1SnippetNode,
): Record<string, unknown> {
  const out = { ...(node as Record<string, unknown>) }
  delete out.type
  delete out.key
  delete out.id
  return out
}

/**
 * Build a v1 snippet reference node from a key (+ optional instance id and
 * overrides). The instance id is emitted as the node's `id`.
 */
export function v1SnippetNode(
  key: string,
  overrides?: Record<string, unknown>,
  instanceId?: string,
): V1SnippetNode {
  return {
    ...(instanceId ? { id: instanceId } : {}),
    type: "snippet",
    key,
    ...(overrides ?? {}),
  }
}
