///|
/// Direct structural representation for debugging/diffing/pretty-printing.
///
/// `pub` makes it readonly outside this package: it can be pattern-matched but
/// not directly constructed. Use the smart constructors below.
///
/// Design notes:
/// - `Record` is encoded as `Record([RecordField(name, value), ...])` (not as an array
///   of `(String, Repr)` pairs) to keep generic traversal/rewrite code simple:
///   every tree edge is a `Repr`, so `children/with_children`, pruning, and diff
///   can work uniformly across node kinds.
/// - Labeled enum arguments are encoded as
///   `Enum(name, [EnumLabeledArg(label, value), ...])`,
///   which supports mixing positional and labeled args.
/// - `Map` is encoded as `Map([MapEntry(key, value), ...])`, which is
///   intended for "map-like" collections (MoonBit map literals like `{ k: v }`).
/// - `Opaque` is intended for "container-like" wrappers that keep a type/tag but
///   otherwise behave structurally through their children.
pub enum Repr {
  UnitLit
  /// Fixed-width integer literal leaf (stored as string representation).
  /// Covers Int16, Int, Int64, UInt16, UInt, UInt64, etc.
  Fixnum(String)
  /// 64-bit floating-point literal leaf.
  DoubleLit(Double)
  /// 32-bit floating-point literal leaf.
  FloatLit(Float)
  /// Boolean literal leaf.
  BoolLit(Bool)
  /// Character literal leaf.
  CharLit(Char)
  /// String literal leaf.
  StringLit(String)
  /// Tuple node. Children are the tuple elements (`Tuple([])` is unit).
  Tuple(Array[Repr])
  /// Array container node.
  Array(Array[Repr])
  /// Record container node. Conventionally contains only `RecordField` children.
  Record(Array[Repr])
  /// Enum constructor/application node: `Enum(name, args)`.
  Enum(String, Array[Repr])
  /// Association/kv container for map literals.
  Map(Array[Repr])
  /// Record field node. Conventionally appears only under `Record`.
  RecordField(String, Repr)
  /// Labeled argument node. Conventionally appears only under `Enum`.
  EnumLabeledArg(String, Repr)
  /// Opaque wrapper node: prints as `` or `` and keeps children.
  Opaque(String, Array[Repr])
  /// Pre-rendered leaf string (already formatted, no quoting/escaping applied).
  Literal(String)
  /// Key/value node. Conventionally appears only under `Map`.
  MapEntry(Repr, Repr)
  /// Pruned subtree marker used by depth-limited pretty-printing.
  Omitted
}

///|
/// Child nodes of a `Repr` node.
///
/// Design notes:
/// - Leaves return `[]`; container nodes return their stored children.
/// - `RecordField`/`EnumLabeledArg` have one child; `MapEntry` has two.
/// - `children` and `with_children` form a partial lens for tree rewrites:
///   `self.with_children(self.children()) == self`.
pub fn Repr::children(self : Repr) -> Array[Repr] {
  match self {
    UnitLit
    | Fixnum(_)
    | DoubleLit(_)
    | FloatLit(_)
    | BoolLit(_)
    | CharLit(_)
    | StringLit(_)
    | Literal(_)
    | Omitted => []
    Tuple(xs)
    | Array(xs)
    | Record(xs)
    | Enum(_, xs)
    | Opaque(_, xs)
    | Map(xs) => xs
    RecordField(_, value) | EnumLabeledArg(_, value) => [value]
    MapEntry(key, value) => [key, value]
  }
}

///|
/// Rebuild a `Repr` node with a new child list (payload is preserved).
///
/// Notes:
/// - Leaf nodes ignore `children` and return themselves.
/// - `RecordField`/`EnumLabeledArg` expect exactly one child and fall back to
///   `RecordField(name, Omitted)`/`EnumLabeledArg(label, Omitted)`.
/// - `MapEntry` expects exactly two children and falls back to `MapEntry(Omitted, Omitted)`.
/// - This is intentionally not a total inverse of `children`: invalid arity is
///   clamped to keep the tree well-formed for generic traversal.
pub fn Repr::with_children(self : Repr, children : Array[Repr]) -> Repr {
  match self {
    UnitLit
    | Fixnum(_)
    | DoubleLit(_)
    | FloatLit(_)
    | BoolLit(_)
    | CharLit(_)
    | StringLit(_)
    | Literal(_)
    | Omitted => self
    Tuple(_) => Tuple(children)
    Array(_) => Array(children)
    Record(_) => Record(children)
    Enum(name, _) => Enum(name, children)
    Opaque(name, _) => Opaque(name, children)
    Map(_) => Map(children)
    RecordField(name, _) =>
      match children {
        [value] => RecordField(name, value)
        _ => RecordField(name, Omitted)
      }
    EnumLabeledArg(label, _) =>
      match children {
        [value] => EnumLabeledArg(label, value)
        _ => EnumLabeledArg(label, Omitted)
      }
    MapEntry(_, _) =>
      match children {
        [key, value] => MapEntry(key, value)
        _ => MapEntry(Omitted, Omitted)
      }
  }
}

///|
/// Traverse a `Repr` tree and rewrite each node with `f`.
///
/// `f` runs after children are traversed (post-order), so it sees rewritten
/// children and can hide fields by rewriting `RecordField`/`EnumLabeledArg` nodes.
pub fn Repr::traverse(self : Repr, f : (Repr) -> Repr) -> Repr {
  fn go(node : Repr) -> Repr {
    let children = node.children()
    let next_children = children.map(fn(child) { go(child) })
    f(node.with_children(next_children))
  }

  go(self)
}

///|
/// A shallow copy of `self` containing only its "label" (children replaced).
///
/// This is used by diff/pretty-print when the structure is preserved but
/// children are rendered separately.
pub fn Repr::shallow(self : Repr) -> Repr {
  match self {
    UnitLit
    | Fixnum(_)
    | DoubleLit(_)
    | FloatLit(_)
    | BoolLit(_)
    | CharLit(_)
    | StringLit(_)
    | Literal(_)
    | Omitted => self
    Tuple(_) => Tuple([])
    Array(_) => Array([])
    Record(_) => Record([])
    Enum(name, _) => Enum(name, [])
    Opaque(name, _) => Opaque(name, [])
    Map(_) => Map([])
    RecordField(name, _) => RecordField(name, Omitted)
    EnumLabeledArg(label, _) => EnumLabeledArg(label, Omitted)
    MapEntry(_, _) => MapEntry(Omitted, Omitted)
  }
}