// VNode - DOM-independent virtual node representation
//
// Type parameter E represents the event type for handlers:
// - Browser: E = DomEvent (or specific event types)
// - SSR: E = Unit (no events)
// - Other platforms: custom event types
//

///|
/// Event handler type - newtype wrapper for callback function
pub struct EventHandler[E] {
  callback : (E) -> Unit
}

///|
/// Create an event handler from a callback.
pub fn[E] handler(f : (E) -> Unit) -> EventHandler[E] {
  { callback: f }
}

///|
/// Get the callback function
pub fn[E] EventHandler::get_callback(self : EventHandler[E]) -> (E) -> Unit {
  self.callback
}

///|
/// Attribute value that can be static or dynamic (signal-based)
/// Type parameter A represents the attribute value type:
/// - Web: A = String (HTML attributes are strings)
/// - TUI: A = TuiAttrValue (typed values like Dimension, Color)
pub(all) enum Attr[E, A] {
  VStatic(A)
  VDynamic(() -> A)
  VHandler(EventHandler[E])
}

///|
/// Virtual DOM node types
/// Type parameter A represents the attribute value type (see Attr[E, A])
pub(all) enum Node[E, A] {
  Element(VElement[E, A])
  Text(String)
  DynamicText(() -> String)
  Fragment(Array[Node[E, A]])
  Show(condition~ : () -> Bool, child~ : () -> Node[E, A])
  For(render~ : () -> Array[Node[E, A]])
  Component(render~ : () -> Node[E, A])
  /// Async node - renders content asynchronously with fallback
  Async(VAsync[E, A])
  /// ErrorBoundary node - catches rendering errors in children
  ErrorBoundary(VErrorBoundary[E, A])
  /// Switch node - renders first matching case or fallback
  Switch(VSwitch[E, A])
  /// Raw HTML string - rendered without escaping
  RawHtml(String)
}

///|
/// Virtual element node
pub struct VElement[E, A] {
  tag : String
  attrs : Array[(String, Attr[E, A])]
  children : Array[Node[E, A]]
}

///|
/// Virtual Async node for async rendering with error handling
pub struct VAsync[E, A] {
  render : async () -> Node[E, A]
  fallback : () -> Node[E, A]
  on_error : ((Error) -> Node[E, A])?
}

///|
/// Virtual ErrorBoundary node for catching rendering errors
pub struct VErrorBoundary[E, A] {
  children : () -> Node[E, A] raise
  fallback : (Error, () -> Unit) -> Node[E, A] raise
}

///|
/// Match case for Switch - pairs a condition with content
pub struct MatchCase[E, A] {
  when : () -> Bool
  render : () -> Node[E, A]
}

///|
/// Virtual Switch node - renders first matching case
pub struct VSwitch[E, A] {
  cases : Array[MatchCase[E, A]]
  fallback : (() -> Node[E, A])?
}

// =============================================================================
// Node constructors
// =============================================================================

///|
/// Create a VNode element.
pub fn[E, A] h(
  tag : String,
  attrs : Array[(String, Attr[E, A])],
  children : Array[Node[E, A]],
) -> Node[E, A] {
  Element({ tag, attrs, children })
}

///|
/// Create a text VNode.
pub fn[E, A] text(content : String) -> Node[E, A] {
  Text(content)
}

///|
/// Create a dynamic text VNode.
pub fn[E, A] text_dyn(content : () -> String) -> Node[E, A] {
  DynamicText(content)
}

///|
/// Create a fragment VNode.
pub fn[E, A] fragment(children : Array[Node[E, A]]) -> Node[E, A] {
  Fragment(children)
}

///|
/// Create a raw HTML VNode (content is not escaped).
pub fn[E, A] raw_html(content : String) -> Node[E, A] {
  RawHtml(content)
}

///|
/// Create a conditional VNode.
pub fn[E, A] show(when : () -> Bool, child : () -> Node[E, A]) -> Node[E, A] {
  Show(condition=when, child~)
}

///|
/// Create a list VNode.
pub fn[E, A] for_each(items : () -> Array[Node[E, A]]) -> Node[E, A] {
  For(render=items)
}

///|
/// Create a component VNode.
pub fn[E, A] component(render : () -> Node[E, A]) -> Node[E, A] {
  Component(render~)
}

///|
/// Create an async VNode with fallback
pub fn[E, A] async_(
  render~ : async () -> Node[E, A],
  fallback~ : () -> Node[E, A],
  on_error? : ((Error) -> Node[E, A])? = None,
) -> Node[E, A] {
  Async({ render, fallback, on_error })
}

///|
/// Create an error boundary VNode
pub fn[E, A] error_boundary(
  children~ : () -> Node[E, A] raise,
  fallback~ : (Error, () -> Unit) -> Node[E, A] raise,
) -> Node[E, A] {
  ErrorBoundary({ children, fallback })
}

///|
/// Create a match case for Switch
pub fn[E, A] match_case(
  when~ : () -> Bool,
  render~ : () -> Node[E, A],
) -> MatchCase[E, A] {
  { when, render }
}

///|
/// Create a switch VNode - renders first matching case
pub fn[E, A] switch_(
  cases~ : Array[MatchCase[E, A]],
  fallback? : (() -> Node[E, A])? = None,
) -> Node[E, A] {
  Switch({ cases, fallback })
}

// =============================================================================
// Attr constructors
// =============================================================================

///|
/// Create a static attribute value.
pub fn[E, A] attr_static(value : A) -> Attr[E, A] {
  VStatic(value)
}

///|
/// Create a dynamic attribute value.
pub fn[E, A] attr_dynamic(getter : () -> A) -> Attr[E, A] {
  VDynamic(getter)
}

///|
/// Create a handler attribute value
pub fn[E, A] attr_handler(handler : EventHandler[E]) -> Attr[E, A] {
  VHandler(handler)
}

///|
/// Create a style attribute value (string form)
pub fn[E] attr_style(style : String) -> Attr[E, String] {
  VStatic(style)
}

///|
/// Create a dynamic style attribute value
pub fn[E] attr_dynamic_style(getter : () -> String) -> Attr[E, String] {
  VDynamic(getter)
}

///|
/// Check if element has dynamic content that needs hydration
pub fn[E, A] has_dynamic_content(attrs : Array[(String, Attr[E, A])]) -> Bool {
  for attr in attrs {
    let (_, value) = attr
    match value {
      VDynamic(_) | VHandler(_) => return true
      _ => ()
    }
  }
  false
}