#!/usr/bin/env bash

UI_ROW_WIDTH=${UI_ROW_WIDTH:-20}
UI_SECTION_WIDTH=${UI_SECTION_WIDTH:-44}

function ui::row() {
  local label="$1" value="$2" width="${3:-$UI_ROW_WIDTH}"
  printf "  %-${width}s %s\n" "${label}:" "$value"
}

function ui::section() {
  local title="$1" width="${2:-$UI_SECTION_WIDTH}"
  local dashes
  dashes=$(printf '─%.0s' $(seq 1 $(( width - ${#title} - 4 ))))
  printf "\n  \033[0;37m── %s %s\033[0m\n" "$title" "$dashes"
}

function ui::list_item() {
  local prefix="$1" value="$2"
  printf "    %s %s\n" "$prefix" "$value"
}

function ui::print_list() {
  local prefix="$1" input="$2"
  [[ -z "$input" ]] && return 0  # early return for empty input
  while IFS= read -r e; do
    [[ -n "$e" ]] && ui::list_item "$prefix" "$e"
  done <<< "$input"
}

function ui::divider() {
  local width="${1:-48}"
  printf "  %s\n" "$(printf '─%.0s' $(seq 1 $width))"
}

function ui::pad() {
  local text="$1" width="${2:-20}"
  local visible
  visible=$(echo -e "$text" | sed 's/\x1b\[[0-9;]*m//g')
  local pad=$(( width - ${#visible} ))
  printf "%b%${pad}s" "$text" ""
}

function ui::pad_mb() {
  local text="$1" width="${2:-20}"
  local visible
  visible=$(printf "%b" "$text" | sed 's/\x1b\[[0-9;]*m//g')
  local vis_len
  vis_len=$(python3 -c "import sys; print(len(sys.stdin.read().rstrip('\n')))" \
    <<< "$visible")
  local pad=$(( width - vis_len ))
  [[ $pad -lt 0 ]] && pad=0
  printf "%b%${pad}s" "$text" ""
}

function ui::vis_len_multi() {
  # Get visible lengths of multiple strings in one Python call
  # Returns newline-separated integers
  python3 -c "
import sys, re
ansi = re.compile(r'\x1b\[[0-9;]*m')
for s in sys.argv[1:]:
    print(len(ansi.sub('', s)))
" "$@"
}

# ui::vis_len <string>  
# Returns the visible (character) length of a string,
# stripping ANSI codes and accounting for multi-byte UTF-8.
function ui::vis_len() {
  local str="${1:-}"
  # Strip ANSI codes first
  local clean
  clean=$(echo "$str" | sed 's/\x1b\[[0-9;]*m//g')
  # Use Python for accurate Unicode character count
  python3 -c "import sys; print(len('${clean//\'/\'\\\'\'}'))" 2>/dev/null || echo "${#clean}"
}

# ui::pad_to_col <string_bytes_printed> <target_visible_col>
# Returns padding needed accounting for UTF-8 byte/char difference.
# extra_bytes = bytes_printed - visible_chars_printed
function ui::utf8_extra_bytes() {
  local str="${1:-}"
  local byte_len=${#str}
  local vis_len
  vis_len=$(ui::vis_len "$str")
  echo $(( byte_len - vis_len ))
}


function ui::pad_status() {
  ui::pad "${1:-}" "${2:-25}" 
}

function ui::center() {
  local text="$1" width="${2:-8}"
  local len=${#text}
  local pad=$(( (width - len) / 2 ))
  local rpad=$(( width - len - pad ))
  printf "%${pad}s%s%${rpad}s" "" "$text" ""
}

# ui::measure_col <data> <field_index> [min_width]
# Scans pipe-delimited data and returns the max visible width
# of the field at field_index (1-based), with optional minimum.
# Strips ANSI codes before measuring.
# Usage:
#   name_width=$(ui::measure_col "$data" 1 10)
#   ip_width=$(ui::measure_col "$data" 2 14)
function ui::measure_col() {
  local data="${1:-}" field_index="${2:-1}" min_width="${3:-0}"
  local max=$min_width
 
  while IFS='|' read -r line; do
    local val
    val=$(echo "$line" | cut -d'|' -f"$field_index")
    # Strip ANSI codes for accurate measurement
    local clean
    clean=$(echo "$val" | sed 's/\x1b\[[0-9;]*m//g')
    local len=${#clean}
    (( len > max )) && max=$len
  done <<< "$data"
 
  echo $max
}
 
# ui::measure_cols <data> <field_indices...>
# Measure multiple columns at once, returns space-separated widths.
# Usage: read -r w1 w2 w3 <<< $(ui::measure_cols "$data" 1 2 3)
function ui::measure_cols() {
  local data="${1:-}"
  shift
  local widths=()
  for idx in "$@"; do
    widths+=("$(ui::measure_col "$data" "$idx")")
  done
  echo "${widths[*]}"
}

function ui::sort_rows() {
  local field="${1:-1}"
  sort -t'|' -k"${field},${field}V"
}

function ui::firewall_rule() {
  local rule="$1"
  if [[ "$rule" =~ ACCEPT|DNAT ]]; then
    printf "\033[0;32m%s\033[0m\n" "$rule"
  elif [[ "$rule" =~ DROP ]]; then
    printf "\033[0;31m%s\033[0m\n" "$rule"
  elif [[ "$rule" =~ NFLOG|LOG ]]; then
    printf "\033[0;37m%s\033[0m\n" "$rule"
  else
    printf "%s\n" "$rule"
  fi
}

# ============================================
# Content Helpers
# ============================================

function ui::has_content() {
  # Returns 0 (true) if content exists, 1 if empty
  # Works with strings, arrays, or command output
  local value="${1:-}"
  [[ -n "$value" ]]
}

function ui::skip_if_empty() {
  # Usage: ui::skip_if_empty "$var" || return 0
  # Or:    ui::skip_if_empty "${array[*]}" || return 0
  local value="${1:-}"
  [[ -z "${value// }" ]] && return 1 || return 0
}

function ui::empty() {
  local val="${1:-}"
  # Empty string or whitespace only
  [[ -z "${val// }" ]] && return 0
  # Numeric zero
  [[ "$val" =~ ^[0-9]+$ ]] && [[ "$val" -eq 0 ]] && return 0
  return 1
}

# Usage: ui::bool "$value" [yes_label] [no_label]
# Default labels: yes / no
function ui::bool() {
  local val="${1:-}" yes="${2:-yes}" no="${3:-no}"
  [[ "$val" == "true" ]] && echo "$yes" || echo "$no"
}

# ============================================
# Prompt
# ============================================

function ui::confirm() {
  local prompt="${1:-Are you sure?}"
  local response
  printf "  %s [y/N] " "$prompt"
  read -r response
  [[ "${response,,}" == "y" || "${response,,}" == "yes" ]]
}