#!/usr/bin/env bash
# commands/list/show.sh

function cmd::list::show::on_load() {
  command::mixin json_output [section="Output"]

  help::section "Filters"
  flag::define --name       value "Show single peer"          label:name      section:Filters
  flag::define --type       value "Filter by device type"     label:type      section:Filters
  flag::define --rule       value "Filter by rule"            label:rule      section:Filters
  flag::define --group      value "Filter by group"           label:group     section:Filters
  flag::define --identity   value "Filter by identity"        label:identity  section:Filters
  flag::define --online     bool  "Show online peers only"                    section:Filters
  flag::define --offline    bool  "Show offline peers only"                   section:Filters
  flag::define --restricted bool  "Show restricted peers"                     section:Filters
  flag::define --blocked    bool  "Show blocked peers"                        section:Filters
  flag::define --allowed    bool  "Show allowed peers"                        section:Filters

  help::section "Output"
  flag::define --detailed   bool  "Show detailed view"                        section:Output

  flag::exclusive --online --offline --blocked --restricted --allowed
}

function cmd::list::show::run() {
  flag::parse "$@" || return 1

  local filter_type;     filter_type=$(flag::value --type)
  local filter_rule;     filter_rule=$(flag::value --rule)
  local filter_group;    filter_group=$(flag::value --group)
  local filter_identity; filter_identity=$(flag::value --identity)
  local single_name;     single_name=$(flag::value --name)
  local online_only=false offline_only=false
  local restricted_only=false blocked_only=false allowed_only=false
  local detailed=false

  flag::bool --online     && online_only=true
  flag::bool --offline    && offline_only=true
  flag::bool --restricted && restricted_only=true
  flag::bool --blocked    && blocked_only=true
  flag::bool --allowed    && allowed_only=true
  flag::bool --detailed   && detailed=true

  if [[ -n "$single_name" ]]; then
    cmd::list::show_client "$single_name"
    return 0
  fi

  local dir
  dir="$(ctx::clients)"
  local confs=("${dir}"/*.conf)
  if [[ ! -f "${confs[0]}" ]]; then
    log::wg_warning "No clients configured"
    return 0
  fi

  cmd::list::_precompute_all

  declare -gA p_identity_filter=()
  if [[ -n "$filter_identity" ]]; then
    identity::require_exists "$filter_identity" || return 1
    while IFS= read -r peer_name; do
      [[ -n "$peer_name" ]] && p_identity_filter["$peer_name"]=1
    done < <(identity::peers "$filter_identity")
    if [[ ${#p_identity_filter[@]} -eq 0 ]]; then
      log::wg_warning "Identity '${filter_identity}' has no peers"
      return 0
    fi
  fi

  log::section "WireGuard Clients"

  local collected_rows=""
  collected_rows=$(cmd::list::_collect_all_rows | ui::sort_rows)
  if [[ -z "$collected_rows" ]]; then
    log::wg_warning "No results found"
    return 0
  fi

  if command::json; then
    cmd::list::_output_json "$collected_rows"
    return 0
  fi

  if $detailed; then
    cmd::list::_render_detailed "$collected_rows"
    cmd::list::_render_summary_from_rows "$collected_rows"
    return 0
  fi

  display::render "peer_list" "$collected_rows" \
    "cmd::list::_render_compact" "cmd::list::_render_table"
}

# ── Helpers ───────────────────────────────────────────────────────────────────

# ============================================
# Detail Card
# ============================================

function cmd::list::show_client() {
  local name="${1:-}"
  local conf
  conf="$(ctx::clients)/${name}.conf"

  if [[ ! -f "$conf" ]]; then
    log::error "Client not found: ${name}"
    return 1
  fi

  local ip
  ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)

  local allowed_ips
  allowed_ips=$(grep "^AllowedIPs" "$conf" | cut -d'=' -f2- | xargs)

  local public_key
  public_key=$(keys::public "$name" 2>/dev/null || echo "unknown")

  local type
  type=$(peers::get_meta "$name" "type" 2>/dev/null)
  [[ -z "$type" ]] && type=$(peers::get_type_from_ip "$ip")

  local endpoint="—"
  local ep
  ep=$(monitor::endpoint_for_key "$public_key")
  [[ -z "$ep" ]] && ep=$(monitor::last_endpoint "$name")
  [[ -n "$ep" ]] && endpoint="$ep"

  local is_blocked="false"
  peers::is_blocked "$name" && is_blocked="true"

  local is_restricted="false"
  peers::is_restricted "$name" && is_restricted="true"

  local handshake_ts
  handshake_ts=$(monitor::get_handshake_ts "$public_key")

  local last_ts
  last_ts=$(monitor::last_attempt "$name")

  local status
  status=$(peers::format_status_verbose "$name" "$public_key" \
    "$is_blocked" "$is_restricted" "$handshake_ts" "$last_ts")

  local last_seen
  last_seen=$(peers::format_last_seen "$name" "$public_key" \
    "$is_blocked" "$last_ts" "" "$handshake_ts")

  local blocks=""
  if block::has_file "$name" && block::is_blocked "$name"; then
    if [[ "$(block::is_blocked_direct "$name")" == "true" ]]; then
      blocks="all traffic blocked"
    fi
    local rule_lines
    rule_lines=$(block::format_rules "$name")
    [[ -n "$rule_lines" ]] && blocks+="$rule_lines"
  fi

  ui::section "Client: ${name}"
  ui::row "IP"         "$ip"
  ui::row "Type"       "$(peers::display_type "$type")"
  ui::row "Status"     "$(echo -e "$status")"
  ui::row "Endpoint"   "$endpoint"
  ui::row "Last seen"  "$last_seen"
  ui::row "AllowedIPs" "$allowed_ips"
  ui::row "Public key" "$public_key"

  if [[ -z "$blocks" ]]; then
    ui::row "Blocks" "none"
  elif [[ "$blocks" == *"all traffic blocked"* ]]; then
    ui::row "Blocks" "$(echo -e "\033[1;31mAll\033[0m")"
  else
    printf "  %-20s\n" "Blocks:"
    echo -e "$blocks"
  fi
  printf "\n"
}
# ============================================
# Row collection (single pass, all filters)
# ============================================

function cmd::list::_collect_all_rows() {
  local dir
  dir="$(ctx::clients)"
  local _verbose_status="${LIST_VERBOSE_STATUS:-true}"
 
  for conf in "${dir}"/*.conf; do
    [[ -f "$conf" ]] || continue
    local client_name
    client_name=$(basename "$conf" .conf)
    [[ -z "$client_name" ]] && continue
 
    if [[ ${#p_identity_filter[@]} -gt 0 && \
          -z "${p_identity_filter[$client_name]:-}" ]]; then
      continue
    fi
 
    local ip="${p_ips[$client_name]:-}"
    [[ -z "$ip" ]] && ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
    [[ -z "$ip" ]] && continue
 
    local type="${p_types[$client_name]:-unknown}"
    [[ -n "$filter_type" && "$type" != "$filter_type" ]] && continue
 
    local pubkey="${p_pubkeys[$client_name]:-}"
    local handshake_ts="${wg_handshakes[$pubkey]:-0}"
    local is_blocked="${p_blocked[$client_name]:-false}"
    local is_restricted="${p_restricted[$client_name]:-false}"
    local last_ts="${p_last_ts[$client_name]:-}"
    local rule="${p_rules[$client_name]:-}"
    local group="${p_main_groups[$client_name]:-}"
 
    if $online_only;     then peers::is_online  "$client_name" "$handshake_ts" "$last_ts" || continue; fi
    if $offline_only;    then peers::is_offline "$client_name" "$handshake_ts" "$last_ts" || continue; fi
    if $restricted_only  && [[ "$is_restricted" != "true" ]]; then continue; fi
    if $blocked_only     && [[ "$is_blocked"    != "true" ]]; then continue; fi
    if $allowed_only && { [[ "$is_blocked" == "true" ]] || \
                          [[ "$is_restricted" == "true" ]]; }; then continue; fi
    if [[ -n "$filter_rule"  && "$rule" != "$filter_rule" ]]; then continue; fi
    if [[ -n "$filter_group" ]]; then
      local all_groups="${peer_group_map[$client_name]:-}"
      [[ "$all_groups" != *"$filter_group"* ]] && continue
    fi
 
    # Resolve status — verbose or simple
    local status
    if [[ "$_verbose_status" == "true" ]]; then
      status=$(peers::format_status_verbose "$client_name" "$pubkey" \
        "$is_blocked" "$is_restricted" "$handshake_ts" "$last_ts" | \
        sed 's/\x1b\[[0-9;]*m//g')
    else
      local state
      state=$(peers::connection_state "$is_blocked" "$is_restricted" "$handshake_ts" "$last_ts")
      status="${state%%|*}"
    fi
 
    # Resolve last seen
    local last_seen="-"
    if [[ "$is_blocked" == "true" && -n "$last_ts" && "$last_ts" != "0" ]]; then
      local attempt_ts
      attempt_ts=$(json::iso_to_ts "$last_ts")
      last_seen="$(fmt::datetime_short "$attempt_ts") (dropped)"
    elif [[ -n "$handshake_ts" && "$handshake_ts" != "0" ]]; then
      local ts_display
      ts_display=$(fmt::datetime_short "$handshake_ts")
      if [[ "$status" == "online"* ]]; then
        last_seen="${ts_display} (handshake)"
      else
        last_seen="$ts_display"
      fi
    fi
 
    printf "%s|%s|%s|%s|%s|%s|%s|%s|%s\n" \
      "$client_name" "$ip" "$type" \
      "${rule:--}" "${group:--}" \
      "$status" "$last_seen" \
      "$is_blocked" "$is_restricted"
  done
}
# ============================================
# Compact render
# ============================================

function cmd::list::_render_compact() {
  local rows="${1:-}"

  # Measure column widths from pure values (fields 1-5, no labels)
  local w_name w_ip w_type w_rule w_group
  w_name=$(ui::measure_col "$rows" 1 14)
  w_ip=$(ui::measure_col   "$rows" 2 13)
  w_type=$(ui::measure_col "$rows" 3 7)
  w_rule=$(ui::measure_col "$rows" 4 4)
  w_group=$(ui::measure_col "$rows" 5 4)

  echo ""
  while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
    [[ -z "$name" ]] && continue
    ui::peer::list_row_compact \
      "$w_name" "$w_ip" "$w_type" "$w_rule" "$w_group" \
      "$name" "$ip" "$type" "$rule" "$group" \
      "$status" "$last_seen" "$is_blocked" "$is_restricted"
  done <<< "$rows"
  echo ""

  cmd::list::_render_summary_from_rows "$rows"
}
# ============================================
# Table render (kept for config switching)
# ============================================

function cmd::list::_render_table() {
  local rows="${1:-}"
  [[ -z "$rows" ]] && log::wg_warning "No results found" && return 0

  # Measure column widths from data (same as compact)
  local w_name=16 w_ip=13 w_type=8 w_rule=10 w_group=10 w_status=10 w_last=20
  while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
    [[ -z "$name" ]] && continue
    (( ${#name}      > w_name   )) && w_name=${#name}
    (( ${#ip}        > w_ip     )) && w_ip=${#ip}
    (( ${#type}      > w_type   )) && w_type=${#type}
    (( ${#rule}      > w_rule   )) && w_rule=${#rule}
    (( ${#group}     > w_group  )) && w_group=${#group}
    (( ${#last_seen} > w_last   )) && w_last=${#last_seen}
    local cs
    cs=$(printf "%s" "$status" | sed 's/\x1b\[[0-9;]*m//g')
    (( ${#cs} > w_status )) && w_status=${#cs}
  done <<< "$rows"
  (( w_name  += 2 )); (( w_ip   += 2 ))
  (( w_type  += 2 )); (( w_rule += 2 ))
  (( w_group += 2 )); (( w_last += 2 ))

  # Header
  printf "\n  %-${w_name}s %-${w_ip}s %-${w_type}s %-${w_rule}s %-${w_group}s %-${w_status}s %s\n" \
    "NAME" "IP" "TYPE" "RULE" "GROUP" "STATUS" "LAST SEEN"
  printf "  %s\n" "$(printf '─%.0s' {1..115})"

  # Rows
  while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
    [[ -z "$name" ]] && continue
    local clean_status
    clean_status=$(echo "$status" | sed 's/\x1b\[[0-9;]*m//g')
    local status_pad_n=$(( w_status - ${#clean_status} ))
    [[ $status_pad_n -lt 0 ]] && status_pad_n=0

    local row_color status_color
    row_color=$(ui::peer::_row_color "$is_blocked" "$is_restricted" "$clean_status")
    status_color=$(ui::peer::status_color "$is_blocked" "$is_restricted" "$clean_status")

    local status_colored="${status_color}${clean_status}\033[0m"

    local last_seen_colored="$last_seen"
    [[ -n "$row_color" ]] && last_seen_colored="${row_color}${last_seen}\033[0m" \
                          || last_seen_colored="${status_color}${last_seen}\033[0m"

    if [[ -n "$row_color" ]]; then
      printf "  %b%-${w_name}s %-${w_ip}s %-${w_type}s %-${w_rule}s %-${w_group}s %-${w_status}s %s\033[0m\n" \
        "$row_color" "$name" "$ip" "$type" "$rule" "$group" "$clean_status" "$last_seen"
    else
      printf "  %-${w_name}s %-${w_ip}s %-${w_type}s %-${w_rule}s %-${w_group}s %b%*s\033[0m %b\n" \
        "$name" "$ip" "$type" "$rule" "$group" \
        "$status_color${clean_status}" "$status_pad_n" "" \
        "$last_seen_colored"
    fi
  done <<< "$rows"

  printf "  %s\n" "$(printf '─%.0s' {1..115})"
  cmd::list::_render_summary_from_rows "$rows"
}

function cmd::list::_iter_confs_table() {
  local dir
  dir="$(ctx::clients)"
  for conf in "${dir}"/*.conf; do
    [[ -f "$conf" ]] || continue
    local client_name
    client_name=$(basename "$conf" .conf)
    [[ -z "$client_name" ]] && continue

    if [[ ${#p_identity_filter[@]} -gt 0 && \
          -z "${p_identity_filter[$client_name]:-}" ]]; then
      continue
    fi

    local ip="${p_ips[$client_name]:-}"
    [[ -z "$ip" ]] && ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)

    local type="${p_types[$client_name]:-unknown}"
    [[ -n "$filter_type" && "$type" != "$filter_type" ]] && continue

    cmd::list::_render_row "$client_name" "$ip" "$type"
  done
}
# ============================================
# Detailed render (grouped by identity)
# ============================================

function cmd::list::_render_detailed() {
  local rows="${1:-}"

  # Measure widths
  local w_name w_ip w_type w_rule w_group w_subnet
  w_name=$(ui::measure_col   "$rows" 1 14)
  w_ip=$(ui::measure_col     "$rows" 2 13)
  w_type=$(ui::measure_col   "$rows" 3 7)
  w_rule=$(ui::measure_col   "$rows" 4 4)
  w_group=$(ui::measure_col  "$rows" 5 4)
  # subnet not in rows — use fixed width
  w_subnet=10

  # Group by identity
  declare -A identity_rows=()
  local no_identity_rows=""

  while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
    [[ -z "$name" ]] && continue
    local id_name
    id_name=$(identity::get_name "$name")
    local row="${name}|${ip}|${type}|${rule}|${group}|${status}|${last_seen}|${is_blocked}|${is_restricted}"
    if [[ -n "$id_name" ]]; then
      identity_rows["$id_name"]+="${row}"$'\n'
    else
      no_identity_rows+="${row}"$'\n'
    fi
  done <<< "$rows"

  echo ""

  # Render identity groups (sorted)
  for id_name in $(echo "${!identity_rows[@]}" | tr ' ' '\n' | sort); do
    ui::peer::list_identity_header "$id_name"
    while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
      [[ -z "$name" ]] && continue

      local peer_type="${p_types[$name]:-}"
      subnet=$(peers::get_display_subnet "$name" "$peer_type")
      
      ui::peer::list_row_detailed \
        "$w_name" "$w_ip" "$w_type" "$w_rule" "$w_group" "$w_subnet" \
        "$name" "$ip" "$type" "$rule" "$group" "$subnet" \
        "$status" "$last_seen" "$is_blocked" "$is_restricted"
    done < <(echo "${identity_rows[$id_name]}" | ui::sort_rows)
  done

  # Render peers without identity (no "other" header if empty)
  if [[ -n "$no_identity_rows" ]]; then
    local trimmed
    trimmed=$(echo "$no_identity_rows" | grep -v '^$')
    if [[ -n "$trimmed" ]]; then
      ui::peer::list_identity_header "other"
      while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
        [[ -z "$name" ]] && continue
        local subnet
        subnet=$(peers::get_meta "$name" "subnet" 2>/dev/null)
        if [[ -z "$subnet" ]]; then
          local peer_type="${p_types[$name]:-}"
          [[ -n "$peer_type" ]] && subnet="$peer_type"
        fi
        [[ -z "$subnet" ]] && subnet="-"
        ui::peer::list_row_detailed \
          "$w_name" "$w_ip" "$w_type" "$w_rule" "$w_group" "$w_subnet" \
          "$name" "$ip" "$type" "$rule" "$group" "$subnet" \
          "$status" "$last_seen" "$is_blocked" "$is_restricted"
      done <<< "$trimmed"
    fi
  fi

  echo ""
}
# ============================================
# Summary
# ============================================

function cmd::list::_render_summary_from_rows() {
  local rows="${1:-}"
  declare -A rule_counts=() group_counts=()
  local total=0
 
  while IFS='|' read -r name ip type rule group rest; do
    [[ -z "$name" ]] && continue
    (( total++ )) || true
    rule_counts["${rule:--}"]=$(( ${rule_counts[${rule:--}]:-0} + 1 )) || true
    [[ "$group" != "-" && -n "$group" ]] && \
      group_counts["$group"]=$(( ${group_counts[$group]:-0} + 1 )) || true
  done <<< "$rows"
 
  local rule_summary=""
  for r in $(echo "${!rule_counts[@]}" | tr ' ' '\n' | sort); do
    rule_summary+="${rule_counts[$r]} ${r}, "
  done
  rule_summary="${rule_summary%, }"
 
  local group_summary=""
  for g in $(echo "${!group_counts[@]}" | tr ' ' '\n' | sort); do
    group_summary+="${group_counts[$g]} in ${g}, "
  done
  group_summary="${group_summary%, }"
 
  if [[ -n "$group_summary" ]]; then
    printf "  Showing %s peers [%s] — %s\n\n" "$total" "$rule_summary" "$group_summary"
  else
    printf "  Showing %s peers [%s]\n\n" "$total" "$rule_summary"
  fi
}
# ============================================
# Table row rendering
# ============================================

function cmd::list::_render_row() {
  local client_name="$1" ip="$2" type="$3"

  local pubkey="${p_pubkeys[$client_name]:-}"
  local handshake_ts="${wg_handshakes[$pubkey]:-0}"
  local is_blocked="${p_blocked[$client_name]:-false}"
  local is_restricted="${p_restricted[$client_name]:-false}"
  local last_ts="${p_last_ts[$client_name]:-}"

  if $online_only;     then peers::is_online  "$client_name" "$handshake_ts" "$last_ts" || return 0; fi
  if $offline_only;    then peers::is_offline "$client_name" "$handshake_ts" "$last_ts" || return 0; fi
  if $restricted_only  && [[ "$is_restricted" != "true" ]]; then return 0; fi
  if $blocked_only     && [[ "$is_blocked"    != "true" ]]; then return 0; fi
  if $allowed_only && { [[ "$is_blocked" == "true" ]] || \
                        [[ "$is_restricted" == "true" ]]; }; then return 0; fi

  if [[ -n "$filter_group" ]]; then
    local all_groups="${peer_group_map[$client_name]:-}"
    [[ "$all_groups" != *"$filter_group"* ]] && return 0
  fi

  local status last_seen display_type rule
  status=$(peers::format_status_verbose "$client_name" "$pubkey" \
    "$is_blocked" "$is_restricted" "$handshake_ts" "$last_ts")
  last_seen=$(peers::format_last_seen "$client_name" "$pubkey" \
    "$is_blocked" "$last_ts" "" "$handshake_ts")
  display_type=$(peers::display_type "$type")
  rule="${p_rules[$client_name]:-—}"

  if [[ -n "$filter_rule" && "$rule" != "$filter_rule" ]]; then return 0; fi

  if [[ "${_list_header_printed:-false}" == "false" ]]; then
    cmd::list::_render_header $has_groups
    _list_header_printed=true
  fi

  rule_counts["$rule"]=$(( ${rule_counts[$rule]:-0} + 1 )) || true

  local padded_status
  padded_status=$(ui::pad_status "$status" 25)

  if $has_groups; then
    local main_group="${p_main_groups[$client_name]:-}"
    local group_display="${main_group:-${peer_group_map[$client_name]:-—}}"
    printf "  %-28s %-15s %-13s %-12s %-12s %s %s\n" \
      "$client_name" "$ip" "$display_type" "$rule" \
      "$group_display" "$padded_status" "$last_seen"
  else
    printf "  %-28s %-15s %-13s %-12s %s %s\n" \
      "$client_name" "$ip" "$display_type" "$rule" \
      "$padded_status" "$last_seen"
  fi
}
# ============================================
# Precompute
# ============================================

function cmd::list::_precompute_all() {
  declare -gA p_ips=() p_rules=() p_types=() p_last_ts=() p_last_evt=() p_main_groups=()
  while IFS="|" read -r name ip rule type last_ts last_evt main_group; do
    [[ -z "$name" ]] && continue
    p_ips["$name"]="$ip"
    p_rules["$name"]="${rule:-}"
    p_types["$name"]="${type:-}"
    p_last_ts["$name"]="$last_ts"
    p_last_evt["$name"]="$last_evt"
    p_main_groups["$name"]="${main_group:-}"
  done < <(json::peer_data "$(ctx::clients)" "$(ctx::meta)" "$(ctx::events_log)")

  for name in "${!p_ips[@]}"; do
    [[ -n "${p_types[$name]:-}" ]] && continue
    p_types["$name"]=$(peers::get_type_from_ip "${p_ips[$name]}")
  done

  declare -gA wg_handshakes=() wg_endpoints=()
  while IFS=$'\t' read -r pubkey ts; do
    [[ -n "$pubkey" ]] && wg_handshakes["$pubkey"]="$ts"
  done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
  while IFS=$'\t' read -r pubkey endpoint; do
    [[ -n "$pubkey" ]] && wg_endpoints["$pubkey"]="$endpoint"
  done < <(wg show "$(config::interface)" endpoints 2>/dev/null)

  declare -gA p_blocked=() p_restricted=()
  cmd::list::_precompute_block_status p_blocked p_restricted

  declare -gA p_pubkeys=()
  local dir
  dir="$(ctx::clients)"
  for kf in "${dir}"/*_public.key; do
    [[ -f "$kf" ]] || continue
    local kname
    kname=$(basename "$kf" _public.key)
    p_pubkeys["$kname"]=$(cat "$kf" 2>/dev/null || echo "")
  done

  has_groups=false
  declare -gA peer_group_map=()
  local groups_dir
  groups_dir="$(ctx::groups)"
  local group_files=("${groups_dir}"/*.group)
  if [[ -f "${group_files[0]}" ]]; then
    has_groups=true
    while IFS=":" read -r peer_name group_name; do
      [[ -n "$peer_name" ]] && peer_group_map["$peer_name"]="$group_name"
    done < <(json::peer_group_map "$groups_dir")
  fi

  # Identity precompute (for --identity filter)
  declare -gA p_identity_filter=()
}

function cmd::list::_precompute_block_status() {
  local -n _blocked="$1"
  local -n _restricted="$2"

  local wg_peers
  wg_peers=$(wg show "$(config::interface)" peers 2>/dev/null)

  while IFS= read -r name; do
    if block::has_specific_rules "$name" 2>/dev/null; then
      _restricted["$name"]=true
    else
      _restricted["$name"]=false
    fi

    local pubkey
    pubkey=$(keys::public "$name" 2>/dev/null || echo "")
    if [[ -n "$pubkey" ]] && ! echo "$wg_peers" | grep -qF "$pubkey"; then
      _blocked["$name"]=true
    else
      _blocked["$name"]=false
    fi
  done < <(peers::all)
}
# ============================================
# Header / Footer (table layout)
# ============================================

function cmd::list::_render_header() {
  ui::peer::list_header_table "$1"
}

function cmd::list::_render_footer() {
  ui::peer::list_footer_table "$1"
}

function cmd::list::_build_group_summary() {
  group_summary=""
  if $has_groups; then
    declare -A _gs=()
    for peer in "${!peer_group_map[@]}"; do
      local g="${peer_group_map[$peer]}"
      _gs["$g"]=$(( ${_gs["$g"]:-0} + 1 )) || true
    done
    for g in "${!_gs[@]}"; do
      group_summary+="${_gs[$g]} in ${g}, "
    done
    group_summary="${group_summary%, }"
  fi
}

function cmd::list::_show_client_safe() {
  local name="$1"
  cmd::list::show_client "$name" || true
}

# ============================================
# JSON (API consumption)
# ============================================

function cmd::list::_output_json() {
  local rows="${1:-}"
  local -a peers=()
 
  while IFS='|' read -r name ip type rule group status last_seen is_blocked is_restricted; do
    [[ -z "$name" ]] && continue
 
    # Escape strings for JSON
    local peer_json
    peer_json=$(printf '{"name":"%s","ip":"%s","type":"%s","rule":"%s","group":"%s","status":"%s","last_seen":"%s","is_blocked":%s,"is_restricted":%s}' \
      "$name" "$ip" "$type" \
      "${rule}" "${group}" \
      "$status" "$last_seen" \
      "$is_blocked" "$is_restricted")
    peers+=("$peer_json")
  done <<< "$rows"
 
  local count=${#peers[@]}
  local array
  # Join array with commas
  array=$(printf '%s\n' "${peers[@]}" | paste -sd ',' -)
  printf '{"peers":[%s]}' "$array" | json::envelope "list" "$count"
}