///|
/// A type-erased modifier that dispatches to concrete modifier implementations
/// via closures. Replaces the 50-variant `ViewModifier[Msg]` enum.
///
/// Each concrete modifier (Padding, Frame, Background, OnTap, etc.) is a
/// standalone type that implements the subset of modifier behavior it needs
/// and converts to `AnyModifier` via a `to_any_modifier` method.
pub(all) struct AnyModifier[Msg] {
  /// Human-readable kind string for identity composition.
  identity_kind : String
  /// Declaration key for identity composition (used by KeyModifier).
  identity_key : Key?
  /// Declaration key for incremental caching.
  declaration : ViewDeclaration
  /// Transform child constraints before passing to child.
  child_constraints : (Constraints) -> Constraints
  /// Transform child environment before passing to child.
  child_environment : (Environment) -> Environment
  /// Transform child style before passing to child.
  child_style : (ViewStyle) -> ViewStyle
  /// Layout the modifier wrapper around the child.
  layout : (ViewLayoutContext) -> ViewLayoutResult
  /// Paint the modifier effect.
  paint : (ViewPaintContext) -> ViewPaintPlan
  /// Handle events intercepted by this modifier.
  event : (ViewEventContext) -> ViewEventResult[Msg]
  /// Semantics info contributed by this modifier.
  semantics : ViewSemanticsInfo
  /// Semantics actions contributed by this modifier.
  semantics_actions : Array[ViewSemanticsActionHandler[Msg]]
  /// Whether this modifier makes the element focusable.
  focusable : Bool
  /// Whether this modifier establishes a keyboard focus scope.
  focus_trap : Bool
  /// Flex weight contributed by this modifier.
  flex_weight : Double
}

///|
/// A modifier that does nothing — used as default for all vtable slots.
pub fn[Msg] noop_modifier() -> AnyModifier[Msg] {
  {
    identity_kind: "",
    identity_key: None,
    declaration: ViewDeclaration::constant(),
    child_constraints: fn(c) { c.loosen() },
    child_environment: fn(e) { e },
    child_style: fn(s) { s },
    layout: fn(ctx) {
      let child_size = match ctx.child_sizes {
        [s, ..] => s
        [] => Size::new(width=0.0, height=0.0)
      }
      let s = ctx.constraints.constrain(child_size)
      ViewLayoutResult::new(size=s, child_frames=[
        Rect::new(x=0.0, y=0.0, width=s.width, height=s.height),
      ])
    },
    paint: fn(_ctx) { ViewPaintPlan::empty() },
    event: fn(_ctx) { ViewEventResult::ignored() },
    semantics: ViewSemanticsInfo::new(),
    semantics_actions: [],
    focusable: false,
    focus_trap: false,
    flex_weight: 0.0,
  }
}

///|
/// ## Trait mapping reference
/// The `AnyModifier` vtable replaces the originally planned trait hierarchy
/// (LayoutModifier, PaintModifier, EventModifier, SemanticsModifier,
/// EnvironmentModifier). Each vtable slot corresponds to a conceptual trait:
///
///   Trait              Vtable slots filled
///   ─────              ───────────────────
///   Layout             child_constraints, layout, flex_weight, declaration
///   Paint              child_style, paint, declaration
///   Event              event
///   Semantics          semantics, semantics_actions, declaration
///   Environment        child_environment, declaration
///   Focus              focusable
///
/// Concrete modifiers fill only the slots they need:
///   PaddingModifier    → Layout only
///   BackgroundModifier → Paint only
///   OnTapModifier      → Event + Semantics + Focus
///   DisabledModifier   → Environment + Paint + Semantics
///
/// When MoonBit supports blanket trait implementations, the vtable can be
/// replaced with proper trait dispatch and generic `ModifierViewNode`.