///|
/// moon-element CLI: generates a custom element skeleton.
///
/// Usage:
/// ```text
/// moon run cmd/main -- x-counter
/// moon run cmd/main -- --help
/// ```

///|
fn usage(cmd : @argparse.Command) -> Unit {
  println(cmd.render_help())
  println("")
  println("Examples:")
  println(
    "  moon run cmd/main -- x-counter                 # shadow DOM (default)",
  )
  println(
    "  moon run cmd/main -- x-panel --mode light      # light DOM + scope_css isolation",
  )
  println(
    "  moon run cmd/main -- layout --width 640        # lay the dashboard out without a browser",
  )
  println(
    "  moon run cmd/main -- layout --format svg       # ... and emit an SVG document",
  )
  println("")
  println(
    "Custom element name rules: must contain a hyphen; only lowercase letters,",
  )
  println(
    "digits, -, ., _; must not start or end with a hyphen; must not start with xml;",
  )
  println("must not be a reserved name.")
}

///|
fn run() -> Unit {
  let cmd = @argparse.Command(
    "moon-element",
    options=[
      @argparse.OptionArg(
        "mode",
        long="mode",
        about="Render mode: shadow (default) or light",
      ),
      @argparse.OptionArg(
        "width",
        long="width",
        about="Page width for `layout` (default 640)",
      ),
      @argparse.OptionArg(
        "format",
        long="format",
        about="`layout` output: json (default) or svg",
      ),
    ],
    positionals=[@argparse.PositionArg("tag")],
    about="Generate a MoonBit custom element skeleton (standard Web Components)",
  )
  let matches = cmd.parse() catch { _ => return usage(cmd) }
  // A misspelled --mode must fail: silently defaulting generates code the caller did not ask for
  let mode = if matches.values is { "mode": [raw, ..], .. } {
    match @core.RenderMode::parse(raw) {
      Some(m) => m
      None => {
        println("Error: --mode only accepts shadow or light, got \"\{raw}\"")
        println("")
        return usage(cmd)
      }
    }
  } else {
    @core.ShadowDom
  }
  if matches.values is { "tag": [tag, ..], .. } {
    if tag == "layout" {
      return run_layout(matches)
    }
    match @core.component_skeleton(tag, mode~) {
      // No trailing newline here (println adds one), so the written file matches what
      // moon fmt normalizes to; the CI drift check relies on that.
      Some(code) => println(code)
      None => {
        println("Error: \"\{tag}\" is not a valid custom element name.")
        println("")
        usage(cmd)
      }
    }
  } else {
    usage(cmd)
  }
}

///|
fn main {
  run()
}

///|
/// 解析 CLI 里的非负小数(例如 640 或 640.5);非法输入返回 None。
fn parse_number(raw : String) -> Double? {
  if raw.length() == 0 {
    return None
  }
  let mut seen_dot = false
  let mut digits = 0
  for ch in raw.iter() {
    if ch == '.' {
      if seen_dot {
        return None
      }
      seen_dot = true
    } else if ch >= '0' && ch <= '9' {
      digits = digits + 1
    } else {
      return None
    }
  }
  if digits == 0 {
    return None
  }
  let mut value = 0.0
  let mut scale = 1.0
  let mut after_dot = false
  for ch in raw.iter() {
    if ch == '.' {
      after_dot = true
      continue
    }
    let digit = (ch.to_int() - '0'.to_int()).to_double()
    if after_dot {
      scale = scale / 10.0
      value = value + digit * scale
    } else {
      value = value * 10.0 + digit
    }
  }
  Some(value)
}

///|
/// `layout`:把内置仪表盘布局成几何,输出确定性 JSON 或 SVG。
fn run_layout(matches : @argparse.Matches) -> Unit {
  let width = if matches.values is { "width": [raw, ..], .. } {
    match parse_number(raw) {
      Some(w) => w
      None => {
        println("Error: --width expects a positive number, got \"\{raw}\"")
        return
      }
    }
  } else {
    640.0
  }
  if width <= 0.0 {
    println("Error: --width must be greater than 0")
    return
  }
  let format = if matches.values is { "format": [raw, ..], .. } {
    raw
  } else {
    "json"
  }
  let (root, box) = @pipeline.run(
    @dashboard.dashboard_view(),
    @dashboard.dashboard_css(),
    width,
    @pipeline.measure_monospace,
  )
  if format == "json" {
    println(@svg.to_json(root, box))
  } else if format == "svg" {
    println(@svg.to_svg(root, box))
  } else {
    println("Error: --format only accepts json or svg, got \"\{format}\"")
  }
}