#!/usr/bin/env bash
# core/framework/help.sh
#
# Dynamic help generation from command::define and flag::define metadata.
#
# Usage in on_load:
#   help::section "Filters"
#   help::section "Output"
#
# Flags assigned to sections via flag::define constraint:
#   flag::define --fw bool "Firewall only" [section="Filters"]
#
# Or via active section (set before flag::define calls):
#   help::section "Filters"
#   command::mixin time_filter [section="Filters"]
#   flag::define --fw bool "Firewall only"   # inherits "Filters"
#
# Auto-generate help:
#   hook::on "command:help" command::help::auto
#
# Or custom:
#   hook::on "command:help" cmd::logs::help

# ── Storage ───────────────────────────────────────────────────────────────────

# Section registry: "ctx:section_name" → order_index
declare -gA _HELP_SECTIONS=()
declare -gi _HELP_SECTION_COUNT=0

# Flag-to-section mapping: "ctx:--flag" → section_name
declare -gA _HELP_FLAG_SECTION=()

# Current active section (set by help::section, used by subsequent flag::define)
declare -g _CURRENT_HELP_SECTION=""

# Command descriptions: "ctx" → description
declare -gA _HELP_CMD_DESC=()

# ── Section registration ──────────────────────────────────────────────────────

# help::section "Section Name"
# Registers a section and sets it as active for subsequent flag::define calls
function help::section() {
  local name="${1:-}"
  [[ -z "$name" ]] && return 1

  local ctx
  ctx="${_CURRENT_COMMAND:-__global__}"
  local key="${ctx}:${name}"

  if [[ -z "${_HELP_SECTIONS[$key]+x}" ]]; then
    _HELP_SECTIONS["$key"]=$(( _HELP_SECTION_COUNT++ ))
  fi

  _CURRENT_HELP_SECTION="$name"
}

# ── Auto help generation ──────────────────────────────────────────────────────

# command::help::auto
# Generates help from registered metadata.
# Called via: hook::on "command:help" command::help::auto
function command::help::auto() {
  local cmd="${1:-}" subcmd="${2:-}"
  local ctx="${_CURRENT_COMMAND:-__global__}"

  local desc="${_HELP_CMD_DESC[$ctx]:-}"

  # Usage line — build from required flags
  local usage_parts=()
  local key
  for key in "${!_FLAG_REGISTRY[@]}"; do
    [[ "$key" != "${ctx}:"* ]] && continue
    local flag="${key#${ctx}:}"
    local reg="${_FLAG_REGISTRY[$key]}"
    local type="${reg%%|*}"
    local constraints
    constraints=$(echo "$reg" | cut -d'|' -f3)

    if [[ -n "$constraints" ]]; then
      local required; required="${_FLAG_C_REQUIRED[$key]:-}"
      local label;    label="${_FLAG_C_LABEL[$key]:-value}"

      [[ -z "$label" ]] && label="value"

      if [[ "$required" == "true" ]]; then
        case "$type" in
          bool)  usage_parts+=("$flag") ;;
          value) usage_parts+=("${flag} <${label}>*") ;;
          array) usage_parts+=("${flag} <${label}>*") ;;
        esac
      fi
    fi
  done

  # Print usage
  local cmd_path="${cmd}"
  local default_subcmd="${_COMMAND_DEFAULT[$cmd]:-}"
  # Only show subcmd in usage if it's not the default
  [[ -n "$subcmd" && "$subcmd" != "$default_subcmd" ]] && cmd_path="${cmd} ${subcmd}"
  printf "\nUsage: wgctl %s" "$cmd_path"
  for part in "${usage_parts[@]:-}"; do
    printf " %s" "$part"
  done

  # Show subcommands if any defined
  local -a subcmds=()
  for key in "${!_COMMAND_DEFS[@]}"; do
    [[ "$key" == "${cmd}:"* ]] && subcmds+=("${key#${cmd}:}")
  done
  [[ ${#subcmds[@]} -gt 0 ]] && printf " [command]"
  printf " [options]\n"

  [[ -n "$desc" ]] && printf "\n%s\n" "$desc"

  # Print subcommands
  if [[ ${#subcmds[@]} -gt 0 ]]; then
    printf "\nCommands:\n"
    for sc in "${subcmds[@]}"; do
      local sc_key="${cmd}:${sc}"
      local sc_desc="${_COMMAND_DEFS[$sc_key]:-}"
      
      local sc_text sc_aliases sc_default
      sc_text=$(echo "$sc_desc"    | cut -d'|' -f1)
      sc_aliases=$(echo "$sc_desc" | cut -d'|' -f2)
      sc_default=$(echo "$sc_desc" | cut -d'|' -f3)

      # Clean up empty aliases
      [[ "$sc_aliases" == "false" || "$sc_aliases" == "true" ]] && sc_aliases=""

      local suffix=""
      [[ "$sc_default" == "true" ]] && suffix=" (default)"
      [[ -n "$sc_aliases" ]] && suffix+="  aliases: ${sc_aliases}"

      printf "  %-12s %s%s\n" "$sc" "$sc_text" "$suffix"
    done
  fi

  # Print flags by section
  # Collect sections for this context, sorted by order
  local -a section_names=()
  local -A section_order=()
  for key in "${!_HELP_SECTIONS[@]}"; do
    [[ "$key" != "${ctx}:"* ]] && continue
    local sname="${key#${ctx}:}"
    section_names+=("$sname")
    section_order["$sname"]="${_HELP_SECTIONS[$key]}"
  done

  # Sort sections by registration order
  local -a sorted_sections=()
  if [[ ${#section_names[@]} -gt 0 ]]; then
    while IFS= read -r sname; do
      sorted_sections+=("$sname")
    done < <(
      for sname in "${section_names[@]}"; do
        echo "${section_order[$sname]} $sname"
      done | sort -n | cut -d' ' -f2-
    )
  fi

  # Flags without a section
  local -a unsectioned=()
  for key in "${!_FLAG_REGISTRY[@]}"; do
    [[ "$key" != "${ctx}:"* ]] && continue
    local flag="${key#${ctx}:}"
    [[ -z "${_HELP_FLAG_SECTION[${ctx}:${flag}]:-}" ]] && unsectioned+=("$flag")
  done

  # Print unsectioned flags first as "Options"
  if [[ ${#unsectioned[@]} -gt 0 ]]; then
    printf "\nOptions:\n"
    for flag in "${unsectioned[@]}"; do
      help::_print_flag "$flag" "$ctx"
    done
  fi

  # Print sectioned flags
  for sname in "${sorted_sections[@]:-}"; do
    local printed_header=false
    for key in "${!_FLAG_REGISTRY[@]}"; do
      [[ "$key" != "${ctx}:"* ]] && continue
      local flag="${key#${ctx}:}"
      local flag_section="${_HELP_FLAG_SECTION[${ctx}:${flag}]:-}"
      [[ "$flag_section" != "$sname" ]] && continue
      if ! $printed_header; then
        printf "\n%s:\n" "$sname"
        printed_header=true
      fi
      help::_print_flag "$flag" "$ctx"
    done
  done

  printf "\n"
}

function help::_print_flag() {
  local flag="$1" ctx="$2"
  local key="${ctx}:${flag}"
  local reg="${_FLAG_REGISTRY[$key]:-}"
  [[ -z "$reg" ]] && return 0

  local type="${reg%%|*}"
  local desc="${reg#*|}"

  # Use pre-cached constraints instead of parsing
  local label="${_FLAG_C_LABEL[$key]:-value}"
  local required="${_FLAG_C_REQUIRED[$key]:-}"
  local default_val="${_FLAG_C_DEFAULT[$key]:-}"
  local choices="${_FLAG_C_CHOICES[$key]:-}"

  local flag_display="$flag"
  case "$type" in
    value) flag_display="${flag} <${label}>" ;;
    array) flag_display="${flag} <${label}>" ;;
  esac

  local suffix=""
  [[ "$required" == "true" ]] && suffix=" *"
  [[ -n "$default_val" && "$type" != "bool" ]] && suffix+="  [default: ${default_val}]"
  [[ -n "$choices" ]] && suffix+="  (${choices//|/|})"

  printf "  %-28s %s%s\n" "$flag_display" "$desc" "$suffix"
}

# ── Reset ─────────────────────────────────────────────────────────────────────

function help::_reset() {
  local ctx="${_CURRENT_COMMAND:-__global__}"
  local key
  for key in "${!_HELP_SECTIONS[@]}"; do
    [[ "$key" == "${ctx}:"* ]] && unset "_HELP_SECTIONS[$key]"
  done
  for key in "${!_HELP_FLAG_SECTION[@]}"; do
    [[ "$key" == "${ctx}:"* ]] && unset "_HELP_FLAG_SECTION[$key]"
  done
  _CURRENT_HELP_SECTION=""
}