// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Render help text for a clap-style command.
fn render_help(cmd : Command) -> String {
  render_help_with_usage_override(cmd, None)
}

///|
fn render_help_with_usage_override(
  cmd : Command,
  usage_line : String?,
) -> String {
  let usage_line = usage_line.unwrap_or("Usage: \{cmd.name}\{usage_tail(cmd)}")
  let about = cmd.about.unwrap_or("")
  let about_section = if about == "" {
    ""
  } else {
    (
      $|
      $|
      $|\{about}
    )
  }
  let commands_section = render_section("Commands:", subcommand_entries(cmd))
  let arguments_section = render_section("Arguments:", positional_entries(cmd))
  let options_section = render_section(
    "Options:",
    option_entries(cmd),
    keep_empty=true,
  )
  let groups_section = render_section("Groups:", group_entries(cmd))
  (
    $|\{usage_line}\{about_section}\{commands_section}\{arguments_section}\{options_section}\{groups_section}
    $|
  )
}

///|
fn usage_tail(cmd : Command) -> String {
  let parts = [
    required_option_usage(cmd),
    if has_options(cmd) {
      "[options]"
    } else {
      ""
    },
    positional_usage(cmd),
    subcommand_usage(cmd),
  ].filter(p => p != "")
  if parts.is_empty() {
    ""
  } else {
    " " + parts.join(" ")
  }
}

///|
fn subcommand_usage(cmd : Command) -> String {
  if !has_subcommands_for_help(cmd) {
    return ""
  }
  if cmd.subcommand_required {
    ""
  } else {
    "[command]"
  }
}

///|
fn has_options(cmd : Command) -> Bool {
  cmd.args.any(arg => {
    !arg.hidden &&
    arg.info is (OptionInfo(long~, short~, ..) | FlagInfo(long~, short~, ..)) &&
    (long is Some(_) || short is Some(_)) &&
    !is_required_arg(arg)
  })
}

///|
fn required_option_usage(cmd : Command) -> String {
  [
    for arg in cmd.args if !(arg.hidden ||
    !is_required_arg(arg) ||
    arg.info is PositionalInfo(_)) &&
    required_option_token(arg) is Some(token) => token
  ].join(" ")
}

///|
fn required_option_token(arg : Arg) -> String? {
  let prefix = match arg.info {
    OptionInfo(long~, short~, ..) | FlagInfo(long~, short~, ..) =>
      if long is Some(long) {
        Some("--\{long}")
      } else if short is Some(short) {
        Some("-\{short}")
      } else {
        None
      }
    PositionalInfo(_) => None
  }
  match prefix {
    Some(prefix) =>
      if arg.info is OptionInfo(_) {
        Some("\{prefix} <\{arg.name}>")
      } else {
        Some(prefix)
      }
    None => None
  }
}

///|
fn positional_usage(cmd : Command) -> String {
  let parts = Array::new(capacity=cmd.args.length())
  for arg in positional_args(cmd.args) {
    if arg.hidden {
      continue
    }
    let required = is_required_arg(arg)
    if arg.multiple {
      if required {
        parts.push("<\{arg.name}...>")
      } else {
        parts.push("[\{arg.name}...]")
      }
    } else if required {
      parts.push("<\{arg.name}>")
    } else {
      parts.push("[\{arg.name}]")
    }
  }
  parts.join(" ")
}

///|
fn option_entries(cmd : Command) -> Array[String] {
  let args = cmd.args
  let display = Array::new(capacity=args.length() + 2)
  let builtin_help_short = help_flag_enabled(cmd) &&
    !has_short_option(args, 'h')
  let builtin_help_long = help_flag_enabled(cmd) &&
    !has_long_option(args, "help")
  let builtin_version_short = version_flag_enabled(cmd) &&
    !has_short_option(args, 'V')
  let builtin_version_long = version_flag_enabled(cmd) &&
    !has_long_option(args, "version")
  let builtin_help_label = builtin_option_label(
    builtin_help_short, builtin_help_long, "-h", "--help",
  )
  if builtin_help_label is Some(label) {
    display.push((label, "Show help information."))
  }
  let builtin_version_label = builtin_option_label(
    builtin_version_short, builtin_version_long, "-V", "--version",
  )
  if builtin_version_label is Some(label) {
    display.push((label, "Show version information."))
  }
  for arg in args {
    guard arg.info
      is (OptionInfo(long~, short~, ..) | FlagInfo(long~, short~, ..)) &&
      (long is Some(_) || short is Some(_)) else {
      continue
    }
    if arg.hidden {
      continue
    }
    let name = if arg.info is OptionInfo(_) {
      "\{arg_display(arg)} <\{arg.name}>"
    } else {
      arg_display(arg)
    }
    display.push((name, arg_doc(arg)))
  }
  format_entries(display)
}

///|
fn has_long_option(args : Array[Arg], name : String) -> Bool {
  args.any(arg => {
    arg.info is (OptionInfo(long~, ..) | FlagInfo(long~, ..)) &&
    long is Some(long) &&
    long == name
  })
}

///|
fn has_short_option(args : Array[Arg], value : Char) -> Bool {
  args.any(arg => {
    arg.info is (OptionInfo(short~, ..) | FlagInfo(short~, ..)) &&
    short is Some(short) &&
    short == value
  })
}

///|
fn builtin_option_label(
  has_short : Bool,
  has_long : Bool,
  short_label : String,
  long_label : String,
) -> String? {
  if has_short && has_long {
    Some("\{short_label}, \{long_label}")
  } else if has_short {
    Some(short_label)
  } else if has_long {
    Some(long_label)
  } else {
    None
  }
}

///|
fn positional_entries(cmd : Command) -> Array[String] {
  format_entries(
    [
      for arg in positional_args(cmd.args) if !arg.hidden => {
        (positional_display(arg), arg_doc(arg))
      }
    ],
  )
}

///|
fn subcommand_entries(cmd : Command) -> Array[String] {
  let display = [
    for sub in cmd.subcommands if !sub.hidden => {
      let doc = sub.about.unwrap_or("")
      let doc = if cmd.default_subcommand is Some(name) && name == sub.name {
        if doc == "" {
          "(default)"
        } else {
          "\{doc} (default)"
        }
      } else {
        doc
      }
      (sub.name, doc)
    }
  ]
  if help_subcommand_enabled(cmd) {
    display.push(("help", "Print help for the subcommand(s)."))
  }
  format_entries(display)
}

///|
fn group_entries(cmd : Command) -> Array[String] {
  let display = Array::new(capacity=cmd.groups.length())
  for group in cmd.groups {
    let members = group_members(cmd, group)
    if members == "" {
      continue
    }
    display.push((group_label(group), members))
  }
  format_entries(display)
}

///|
fn render_section(
  header : String,
  lines : Array[String],
  keep_empty? : Bool = false,
) -> String {
  if lines.is_empty() {
    if keep_empty {
      (
        $|
        $|
        $|\{header}
      )
    } else {
      ""
    }
  } else {
    let body = lines.join("\n")
    (
      $|
      $|
      $|\{header}
      $|\{body}
    )
  }
}

///|
fn format_entries(display : Array[(String, String)]) -> Array[String] {
  let max_len = display.fold(init=0, (acc, item) => acc.max(item.0.length()))
  fn fmt(name : String, doc : String) -> String {
    let padding = " ".repeat(max_len - name.length() + 2)
    "  \{name}\{padding}\{doc}"
  }
  [
    for item in display => fmt(item.0, item.1)
  ]
}

///|
fn arg_display(arg : Arg) -> String {
  let parts = Array::new(capacity=2)
  let (short, long) = match arg.info {
    OptionInfo(short~, long~, ..)
    | FlagInfo(short~, long~, ..)
    | (PositionalInfo(_) with short = None, long = None) => (short, long)
  }
  if short is Some(short) {
    parts.push("-\{short}")
  }
  if long is Some(long) {
    if arg.info is FlagInfo(negatable=true, ..) {
      parts.push("--[no-]\{long}")
    } else {
      parts.push("--\{long}")
    }
  }
  if parts.is_empty() {
    arg.name
  } else {
    parts.join(", ")
  }
}

///|
fn positional_display(arg : Arg) -> String {
  if arg.multiple {
    "\{arg.name}..."
  } else {
    arg.name
  }
}

///|
fn arg_doc(arg : Arg) -> String {
  let notes = []
  if arg.env is Some(env_name) {
    notes.push("[env: \{env_name}]")
  }
  if arg.info
    is (OptionInfo(default_values~, ..) | PositionalInfo(default_values~, ..)) &&
    default_values is Some(values) {
    if !values.is_empty() {
      let defaults = values.join(", ")
      notes.push("[default: \{defaults}]")
    }
  }
  let help = arg.about.unwrap_or("")
  if help == "" {
    notes.join(" ")
  } else if !notes.is_empty() {
    let notes_text = notes.join(" ")
    "\{help} \{notes_text}"
  } else {
    help
  }
}

///|
fn has_subcommands_for_help(cmd : Command) -> Bool {
  help_subcommand_enabled(cmd) || cmd.subcommands.any(sub => !sub.hidden)
}

///|
fn is_required_arg(arg : Arg) -> Bool {
  if arg.required {
    true
  } else {
    let (min, _) = arg_min_max(arg)
    min > 0
  }
}

///|
fn group_label(group : ArgGroup) -> String {
  let tags = []
  if group.required {
    tags.push("[required]")
  }
  if !group.multiple {
    tags.push("[exclusive]")
  }
  if tags.is_empty() {
    group.name
  } else {
    let tags_text = tags.join(" ")
    "\{group.name} \{tags_text}"
  }
}

///|
fn group_members(cmd : Command, group : ArgGroup) -> String {
  [
    for arg in cmd.args if !arg.hidden && arg_in_group(arg, group) => {
      group_member_display(arg)
    }
  ].join(", ")
}

///|
fn group_member_display(arg : Arg) -> String {
  let base = arg_display(arg)
  match arg.info {
    FlagInfo(_) => base
    OptionInfo(_) => "\{base} <\{arg.name}>"
    PositionalInfo(_) => base
  }
}