import { _render } from "agency-lang/stdlib-lib/layout.js"

/** @module
  @summary Build terminal layouts from boxes, rows, columns, and styled text.
  Build terminal output as a tree of boxes, rows, and columns, then render
  it to a styled string you can print. Handy for splash screens, summary
  panels, and side-by-side columns.

  ```ts
  import { box, render } from "std::ui/layout"

  const panel = box(title: "Hello", padding: 1) as b {
    b.text("Welcome!", bold: true)
    b.text("How are you?")
  }
  print(render(panel))
  ```

  Containers (`box`, `row`, `column`) take a `width`: a number of columns,
  `"50%"` of the parent, or `"full"` for the parent width (the terminal at the root). Content wraps to
  fit its container; pass `wrap: false` to `raw` to preserve exact layout
  (ASCII art, pre-rendered tables). Unsized containers shrink to fit their
  content but cap at the available width, wrapping anything longer.

  For prompts that read a key or a line, use `std::ui/cli`.
*/

/**
Every layout node has the same shape. Containers carry their
content in `children`; leaves have `children: []`.
*/
export type LayoutNode = {
  type: string;
  attrs: any;
  children: any[]
}

/**
Methods inside a container's trailing `as name { ... }` block. Each
method constructs a leaf or container and pushes it onto the
surrounding container's `children` array.
*/
export type LayoutBuilder = {
  text: any;
  raw: any;
  space: any;
  hline: any;
  vline: any;
  row: any;
  column: any;
  box: any
}

export type Alignment = "start" | "center" | "end"

export type BorderStyle = "rounded" | "heavy" | "double" | "light" | "none"

/** A column count, a percentage such as "50%", or "full" for the parent width. */
export type Width = number | "full" | string

// ------------------------------------------------------------------
// Top-level leaf constructors. Each returns a LayoutNode without
// touching any parent, so it is safe to call from anywhere (Agency
// code, LLM tool calls, ad-hoc construction).
// ------------------------------------------------------------------

export def text(
  content: string,
  fgColor: string = "",
  bgColor: string = "",
  bold: boolean = false,
  italic: boolean = false,
  dim: boolean = false,
  underline: boolean = false,
  align: Alignment = "start",
): LayoutNode {
  """
  A styled run of text. Newlines split it into multiple lines.

  @param fgColor - Foreground color, named ("red") or hex ("#cc7a4a")
  @param bgColor - Background color, named or hex
  @param dim - Use reduced intensity
  @param align - For multi-line text, how shorter lines align to the longest
  """
  return {
    type: "text",
    attrs: {
      content: content,
      fgColor: fgColor,
      bgColor: bgColor,
      bold: bold,
      italic: italic,
      dim: dim,
      underline: underline,
      align: align
    },
    children: []
  }
}

/** Use `wrap: false` for ASCII art or tables whose spacing must stay intact.
Wrapping preserves ANSI colors without bleeding into surrounding borders. */
export def raw(content: string, align: Alignment = "start", wrap: boolean = true): LayoutNode {
  """
  Build a layout node from text with existing ANSI styling or newlines.

  @param align - How shorter lines align to the longest
  @param wrap - Reflow to the container width; false preserves exact spacing
  """
  return {
    type: "raw",
    attrs: {
      content: content,
      align: align,
      wrap: wrap
    },
    children: []
  }
}

export def space(count: number = 1): LayoutNode {
  """
  Blank space. Inside a row it adds columns; inside a column, rows.

  @param count - Number of cells of blank space
  """
  return {
    type: "space",
    attrs: {
      count: count
    },
    children: []
  }
}

export def hline(
  char: string = "─",
  length: number = 0,
  fgColor: string = "",
  bold: boolean = false,
  dim: boolean = false,
): LayoutNode {
  """
  A horizontal rule. Inside a column, leave length at 0 to span the column's width.

  @param char - The character to repeat
  @param length - Explicit length (0 lets the parent size it)
  @param fgColor - Color of the line
  @param dim - Use reduced intensity
  """
  return {
    type: "hline",
    attrs: {
      char: char,
      length: length,
      fgColor: fgColor,
      bold: bold,
      dim: dim
    },
    children: []
  }
}

export def vline(
  char: string = "│",
  length: number = 0,
  fgColor: string = "",
  bold: boolean = false,
  dim: boolean = false,
): LayoutNode {
  """
  A vertical rule. Inside a row, leave length at 0 to span the row's height.

  @param char - The character to repeat
  @param length - Explicit length (0 lets the parent size it)
  @param fgColor - Color of the line
  @param dim - Use reduced intensity
  """
  return {
    type: "vline",
    attrs: {
      char: char,
      length: length,
      fgColor: fgColor,
      bold: bold,
      dim: dim
    },
    children: []
  }
}

// ------------------------------------------------------------------
// Builder helpers. Each `_add*` constructs the corresponding node via
// the top-level constructor above, then pushes it onto `kids`. They
// exist solely so `_makeBuilder` can `.partial(kids: kids)` them.
// ------------------------------------------------------------------

def _addText(
  kids: any[],
  content: string,
  fgColor: string = "",
  bgColor: string = "",
  bold: boolean = false,
  italic: boolean = false,
  dim: boolean = false,
  underline: boolean = false,
  align: Alignment = "start",
): LayoutNode {
  const n = text(
    content: content,
    fgColor: fgColor,
    bgColor: bgColor,
    bold: bold,
    italic: italic,
    dim: dim,
    underline: underline,
    align: align,
  )
  kids.push(n)
  return n
}

def _addRaw(
  kids: any[],
  content: string,
  align: Alignment = "start",
  wrap: boolean = true,
): LayoutNode {
  const n = raw(content: content, align: align, wrap: wrap)
  kids.push(n)
  return n
}

def _addSpace(kids: any[], count: number = 1): LayoutNode {
  const n = space(count: count)
  kids.push(n)
  return n
}

def _addHline(
  kids: any[],
  char: string = "─",
  length: number = 0,
  fgColor: string = "",
  bold: boolean = false,
  dim: boolean = false,
): LayoutNode {
  const n = hline(char: char, length: length, fgColor: fgColor, bold: bold, dim: dim)
  kids.push(n)
  return n
}

def _addVline(
  kids: any[],
  char: string = "│",
  length: number = 0,
  fgColor: string = "",
  bold: boolean = false,
  dim: boolean = false,
): LayoutNode {
  const n = vline(char: char, length: length, fgColor: fgColor, bold: bold, dim: dim)
  kids.push(n)
  return n
}

def _addRow(
  kids: any[],
  gap: number = 0,
  align: Alignment = "start",
  width: Width = null,
  children: LayoutNode[] = null,
  block: (LayoutBuilder) -> void = null,
): LayoutNode {
  const n = row(gap: gap, align: align, width: width, children: children, block: block)
  kids.push(n)
  return n
}

def _addColumn(
  kids: any[],
  gap: number = 0,
  align: Alignment = "start",
  width: Width = null,
  children: LayoutNode[] = null,
  block: (LayoutBuilder) -> void = null,
): LayoutNode {
  const n = column(gap: gap, align: align, width: width, children: children, block: block)
  kids.push(n)
  return n
}

def _addBox(
  kids: any[],
  title: string = "",
  titleColor: string = "",
  borderStyle: BorderStyle = "rounded",
  borderColor: string = "",
  padding: number = 1,
  width: Width = null,
  children: LayoutNode[] = null,
  block: (LayoutBuilder) -> void = null,
): LayoutNode {
  const n = box(
    title: title,
    titleColor: titleColor,
    borderStyle: borderStyle,
    borderColor: borderColor,
    padding: padding,
    width: width,
    children: children,
    block: block,
  )
  kids.push(n)
  return n
}

def _makeBuilder(kids: any[]): LayoutBuilder {
  return {
    text: _addText.partial(kids: kids),
    raw: _addRaw.partial(kids: kids),
    space: _addSpace.partial(kids: kids),
    hline: _addHline.partial(kids: kids),
    vline: _addVline.partial(kids: kids),
    row: _addRow.partial(kids: kids),
    column: _addColumn.partial(kids: kids),
    box: _addBox.partial(kids: kids)
  }
}

// ------------------------------------------------------------------
// Container constructors. Each accepts either `children: [...]`, a
// trailing builder block, both (children prepended, block
// appends after), or neither (empty container).
// ------------------------------------------------------------------

export def row(
  gap: number = 0,
  align: Alignment = "start",
  width: Width = null,
  children: LayoutNode[] = null,
  block: (LayoutBuilder) -> void = null,
): LayoutNode {
  """
  A horizontal container. Children render left to right.

  @param gap - Cells of blank space between children
  @param align - Vertical alignment of shorter children
  @param width - Columns, a percentage of the parent, or "full" for the parent width
  @param children - Pre-built child nodes
  @param block - Trailing builder block, appended after children
  """
  const kids: any[] = []
  if (children != null) {
    for (c in children) {
      kids.push(c)
    }
  }
  if (block != null) {
    block(_makeBuilder(kids))
  }
  const attrs: any = {
    gap: gap,
    align: align
  }
  if (width != null) {
    attrs.width = width
  }
  return {
    type: "row",
    attrs: attrs,
    children: kids
  }
}

export def column(
  gap: number = 0,
  align: Alignment = "start",
  width: Width = null,
  children: LayoutNode[] = null,
  block: (LayoutBuilder) -> void = null,
): LayoutNode {
  """
  A vertical container. Children render top to bottom.

  @param gap - Blank rows between children
  @param align - Horizontal alignment of narrower children
  @param width - Columns, a percentage of the parent, or "full" for the parent width
  @param children - Pre-built child nodes
  @param block - Trailing builder block, appended after children
  """
  const kids: any[] = []
  if (children != null) {
    for (c in children) {
      kids.push(c)
    }
  }
  if (block != null) {
    block(_makeBuilder(kids))
  }
  const attrs: any = {
    gap: gap,
    align: align
  }
  if (width != null) {
    attrs.width = width
  }
  return {
    type: "column",
    attrs: attrs,
    children: kids
  }
}

export def box(
  title: string = "",
  titleColor: string = "",
  borderStyle: BorderStyle = "rounded",
  borderColor: string = "",
  padding: number = 1,
  width: Width = null,
  children: LayoutNode[] = null,
  block: (LayoutBuilder) -> void = null,
): LayoutNode {
  """
  A bordered panel. Multiple children stack vertically inside it.

  @param title - Text shown in the top border (empty for none)
  @param titleColor - Color of the title text
  @param borderStyle - The border character style; "none" draws no
    frame (padding and title still apply, the title as a plain first
    line)
  @param borderColor - Color of the border
  @param padding - Cells of padding between the border and content
  @param width - Columns, a percentage of the parent, or "full" for the parent width
  @param children - Pre-built child nodes
  @param block - Trailing builder block, appended after children
  """
  const kids: any[] = []
  if (children != null) {
    for (c in children) {
      kids.push(c)
    }
  }
  if (block != null) {
    block(_makeBuilder(kids))
  }
  const attrs: any = {
    title: title,
    titleColor: titleColor,
    borderStyle: borderStyle,
    borderColor: borderColor,
    padding: padding
  }
  if (width != null) {
    attrs.width = width
  }
  return {
    type: "box",
    attrs: attrs,
    children: kids
  }
}

@capabilities(terminal)
export def render(
  node: LayoutNode,
  color: "auto" | boolean = "auto",
  cols: number = 0,
  rows: number = 0,
): string {
  """
  Render a layout tree to a styled, multi-line string ready to print.

  @param color - "auto" uses terminal color support; true always; false strips styling
  @param cols - Width override in columns (0 auto-detects)
  @param rows - Height override in rows (0 uses the default)
  """
  return _render(node, color, cols, rows)
}
