///|
/// Build a renderer context from viewport and color mode
pub fn create_render_context(
  viewport_width : Int,
  viewport_height : Int,
  dark_mode : Bool,
) -> @renderer.RenderContext {
  let color_scheme : @css.ColorScheme = if dark_mode {
    @css.ColorScheme::Dark
  } else {
    @css.ColorScheme::Light
  }
  {
    viewport_width: viewport_width.to_double(),
    viewport_height: viewport_height.to_double(),
    root_font_size: 16.0,
    color_scheme,
  }
}

///|
/// Parse CSS with diagnostics and print an aggregated support report
pub fn report_css_support(css_list : Array[String]) -> Unit {
  // Aggregate summaries from all stylesheets
  let mut total = 0
  let mut supported = 0
  let mut partial = 0
  let mut unsupported_layout = 0
  let mut unsupported_visual = 0
  let mut deprecated = 0
  let mut unknown = 0
  let all_diagnostics : Array[@css.Diagnostic] = []
  let seen : Map[String, Bool] = {}
  // Parse all CSS with diagnostics
  for css in css_list {
    let result = @css.parse_stylesheet_with_diagnostics(css)
    let summary = result.diagnostics.get_summary()
    // Aggregate counts
    total += summary.total_properties
    supported += summary.supported
    partial += summary.partial
    unsupported_layout += summary.unsupported_layout
    unsupported_visual += summary.unsupported_visual
    deprecated += summary.deprecated
    unknown += summary.unknown
    // Collect unique diagnostics (non-supported only)
    for diag in result.diagnostics.get_diagnostics() {
      let key = diag.property + ":" + diag.value
      if !seen.contains(key) {
        seen.set(key, true)
        all_diagnostics.push(diag)
      }
    }
  }
  // Print compact summary
  let buf = StringBuilder::new()
  buf.write_string("\nCSS: ")
  buf.write_string(supported.to_string())
  buf.write_string(" ok")
  if partial > 0 {
    buf.write_string(", ")
    buf.write_string(partial.to_string())
    buf.write_string(" partial")
  }
  if unsupported_visual > 0 {
    buf.write_string(", ")
    buf.write_string(unsupported_visual.to_string())
    buf.write_string(" visual-only")
  }
  if unsupported_layout > 0 {
    buf.write_string(", ")
    buf.write_string(unsupported_layout.to_string())
    buf.write_string(" layout!")
  }
  if unknown > 0 {
    buf.write_string(", ")
    buf.write_string(unknown.to_string())
    buf.write_string(" unknown!")
  }
  println(buf.to_string())
  // Print detailed report if there are issues
  if unsupported_layout > 0 || unknown > 0 {
    println("=== CSS Support Report ===")
    println("Total properties: " + total.to_string())
    println("  Supported: " + supported.to_string())
    if partial > 0 {
      println("  Partial: " + partial.to_string())
    }
    if deprecated > 0 {
      println("  Deprecated: " + deprecated.to_string())
    }
    if unsupported_visual > 0 {
      println("  Unsupported (visual): " + unsupported_visual.to_string())
    }
    if unsupported_layout > 0 {
      println(
        "  Unsupported (layout): " +
        unsupported_layout.to_string() +
        " [WARNING]",
      )
    }
    if unknown > 0 {
      println("  Unknown: " + unknown.to_string() + " [ERROR]")
    }
    // Print first few layout-affecting issues
    let layout_issues : Array[@css.Diagnostic] = []
    for diag in all_diagnostics {
      if @css.affects_layout(diag.level) {
        layout_issues.push(diag)
      }
    }
    if layout_issues.length() > 0 {
      println("\n--- Layout-affecting issues ---")
      let count = if layout_issues.length() > 10 {
        10
      } else {
        layout_issues.length()
      }
      for i = 0; i < count; i = i + 1 {
        let diag = layout_issues[i]
        println("  " + diag.property + ": " + diag.value)
      }
      if layout_issues.length() > 10 {
        println(
          "  ... and " + (layout_issues.length() - 10).to_string() + " more",
        )
      }
    }
    // Print first few unknown properties
    let unknown_props : Array[@css.Diagnostic] = []
    for diag in all_diagnostics {
      match diag.level {
        @css.SupportLevel::Unknown => unknown_props.push(diag)
        _ => ()
      }
    }
    if unknown_props.length() > 0 {
      println("\n--- Unknown properties ---")
      let count = if unknown_props.length() > 10 {
        10
      } else {
        unknown_props.length()
      }
      for i = 0; i < count; i = i + 1 {
        let diag = unknown_props[i]
        println("  " + diag.property + ": " + diag.value)
      }
      if unknown_props.length() > 10 {
        println(
          "  ... and " + (unknown_props.length() - 10).to_string() + " more",
        )
      }
    }
  }
}

///|
fn default_extract_config(
  viewport_width : Int,
  viewport_height : Int,
) -> @arc90.ExtractConfig {
  {
    min_text_length: 50,
    viewport_width: viewport_width.to_double(),
    viewport_height: viewport_height.to_double(),
    ad_sizes: [
      (300.0, 250.0),
      (728.0, 90.0),
      (160.0, 600.0),
      (300.0, 600.0),
      (320.0, 50.0),
      (320.0, 100.0),
    ],
  }
}

///|
/// Run Arc90 extraction and return summary
pub fn render_arc90_summary(
  tree : @aom.AccessibilityTree,
  viewport_width : Int,
  viewport_height : Int,
) -> String {
  let result = @arc90.extract_content(
    tree,
    default_extract_config(viewport_width, viewport_height),
  )
  result.to_summary()
}

///|
/// Extract main content text from accessibility tree
pub fn extract_main_text(
  tree : @aom.AccessibilityTree,
  viewport_width : Int,
  viewport_height : Int,
) -> String {
  let result = @arc90.extract_content(
    tree,
    default_extract_config(viewport_width, viewport_height),
  )
  match result.main_content {
    Some(node) => collect_node_text(node)
    None => "(No main content found)"
  }
}

///|
fn collect_node_text(node : @aom.AccessibilityNode) -> String {
  let buf = StringBuilder::new()
  collect_node_text_recursive(node, buf)
  buf.to_string().trim().to_string()
}

///|
fn collect_node_text_recursive(
  node : @aom.AccessibilityNode,
  buf : StringBuilder,
) -> Unit {
  // Skip navigation elements
  if node.role == @aom.Navigation ||
    node.role == @aom.Banner ||
    node.role == @aom.ContentInfo {
    return
  }
  // Add text content if present
  match node.text {
    Some(text) if text.length() > 0 => {
      buf.write_string(text)
      // Add appropriate spacing based on role
      match node.role {
        @aom.Heading | @aom.Paragraph | @aom.ListItem | @aom.Article =>
          buf.write_string("\n\n")
        _ => buf.write_string(" ")
      }
    }
    _ =>
      // If no text, try name (for links, buttons, etc.)
      match node.name {
        Some(name) if name.length() > 0 && node.children.length() == 0 => {
          buf.write_string(name)
          buf.write_string(" ")
        }
        _ => ()
      }
  }
  // Recurse into children
  for child in node.children {
    collect_node_text_recursive(child, buf)
  }
}

///|
/// Run visual grounding demo and return formatted output
pub fn render_grounding_demo(
  tree : @aom.AccessibilityTree,
  viewport_width : Int,
  viewport_height : Int,
) -> String {
  let viewport : @grounding.Viewport = {
    width: viewport_width.to_double(),
    height: viewport_height.to_double(),
  }
  let buf = StringBuilder::new()
  buf.write_string("=== Visual Grounding Demo ===\n")
  buf.write_string("Viewport: ")
  buf.write_string(viewport_width.to_string())
  buf.write_string("x")
  buf.write_string(viewport_height.to_string())
  buf.write_string("\n\n")

  // Test different spatial queries
  let regions : Array[(@grounding.SpatialRegion, String)] = [
    (@grounding.TopLeft, "Top Left"),
    (@grounding.TopRight, "Top Right"),
    (@grounding.Center, "Center"),
    (@grounding.BottomLeft, "Bottom Left"),
    (@grounding.BottomRight, "Bottom Right"),
  ]
  for region_info in regions {
    let (region, name) = region_info
    buf.write_string("--- ")
    buf.write_string(name)
    buf.write_string(" ---\n")
    let results = @grounding.find_by_spatial(
      tree,
      @grounding.InRegion(region),
      viewport,
    )
    if results.length() == 0 {
      buf.write_string("  (no interactive elements)\n")
    } else {
      let limit = if results.length() < 3 { results.length() } else { 3 }
      for i in 0..