#!/usr/bin/env bash
# hosts.command.sh — manage host/IP display name mappings

# ============================================
# Lifecycle
# ============================================

function cmd::hosts::on_load() {
  flag::register --ip
  flag::register --subnet
  flag::register --port
  flag::register --name
  flag::register --desc
  flag::register --tag
  flag::register --tags
  flag::register --force

  command::mixin json_output
}

# ============================================
# Help
# ============================================

function cmd::hosts::help() {
  cat <<EOF
Usage: wgctl hosts <subcommand> [options]

Manage host display names for IP resolution in logs, watch, and activity.
Maps IPs, subnets, and ports to human-readable names.

Subcommands:
  list                        List all host entries
  show --ip <ip>              Show host entry details
  show --subnet <cidr>        Show subnet entry details
  show --port <port>          Show port entry details
  add --ip <ip> --name <name> Add a host entry
  add --subnet <cidr> --name <name>
                              Add a subnet entry
  add --port <port> --name <name>
                              Add a port entry
  rm --ip <ip>                Remove a host entry
  rm --subnet <cidr>          Remove a subnet entry
  rm --port <port>            Remove a port entry

Options for add:
  --ip <ip>                   IP address to map
  --subnet <cidr>             Subnet CIDR to map (e.g. 10.0.0.0/24)
  --port <port>               Port number to map (e.g. 443)
  --name <name>               Display name (e.g. vodafone-wan)
  --desc <description>        Optional description
  --tag <tag>                 Tag (repeatable)
  --tags <tag1,tag2>          Tags (comma-separated)

Options for rm:
  --force                     Skip confirmation

Examples:
  wgctl hosts list
  wgctl hosts add --ip 148.69.46.73 --name vodafone-wan --desc "Vodafone WAN"
  wgctl hosts add --ip 94.63.0.129 --name nuno-home --tags home,isp
  wgctl hosts add --subnet 10.0.0.0/24 --name lan --desc "Local LAN"
  wgctl hosts add --port 443 --name https
  wgctl hosts show --ip 148.69.46.73
  wgctl hosts rm --ip 148.69.46.73
EOF
}

# ============================================
# Run
# ============================================

function cmd::hosts::run() {
  local subcmd="${1:-list}"
  shift || true

  if command::json; then
    cmd::hosts::_output_json
    return 0
  fi

  case "$subcmd" in
    list)          cmd::hosts::list "$@" ;;
    show)          cmd::hosts::show "$@" ;;
    add)           cmd::hosts::add  "$@" ;;
    rm|remove|del) cmd::hosts::rm   "$@" ;;
    help)          cmd::hosts::help      ;;
    *)
      log::error "Unknown subcommand: '${subcmd}'"
      cmd::hosts::help
      return 1 ;;
  esac
}

# ============================================
# List
# ============================================

function cmd::hosts::list() {
  local filter_tag=""
 
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --tag)  filter_tag="$2"; shift 2 ;;
      --help) cmd::hosts::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done
 
  local hosts_file
  hosts_file="$(ctx::hosts)"
 
  if [[ ! -f "$hosts_file" ]]; then
    log::wg_warning "No hosts configured. Use 'wgctl hosts add' to add one."
    return 0
  fi
 
  local data
  data=$(json::hosts_list "$hosts_file" 2>/dev/null)
  [[ -z "$data" ]] && log::wg_warning "No hosts configured." && return 0
 
  # Apply tag filter to data first
  local filtered_data=""
  while IFS='|' read -r type key name desc tags; do
    [[ -z "$type" ]] && continue
    [[ -n "$filter_tag" && "$tags" != *"$filter_tag"* ]] && continue
    filtered_data+="${type}|${key}|${name}|${desc}|${tags}"$'\n'
  done <<< "$data"
 
  [[ -z "$filtered_data" ]] && log::wg_warning "No hosts found." && return 0
 
  # Measure column widths from filtered data
  local w_key=15 w_name=16 w_desc=10
  while IFS='|' read -r type key name desc tags; do
    [[ -z "$type" ]] && continue
    (( ${#key}  > w_key  )) && w_key=${#key}
    (( ${#name} > w_name )) && w_name=${#name}
    local desc_len=${#desc}
    [[ -z "$desc" ]] && desc_len=1  # "—" = 1 visible char
    (( desc_len > w_desc )) && w_desc=$desc_len
  done <<< "$filtered_data"
  (( w_key  += 2 ))
  (( w_name += 2 ))
  (( w_desc += 2 ))
 
  log::section "Host Mappings"
  echo ""
 
  if display::is_table "hosts_list"; then
    cmd::hosts::_render_table "$data"
    return 0
  fi

  local last_type="" found=false
  while IFS='|' read -r type key name desc tags; do
    [[ -z "$type" ]] && continue
    found=true
 
    # Section header when type changes
    if [[ "$type" != "$last_type" ]]; then
      [[ -n "$last_type" ]] && echo ""
      ui::hosts::section_header "$type"
      last_type="$type"
    fi
 
    ui::hosts::list_row "$type" "$key" "$name" "$desc" "$tags" \
      "$w_key" "$w_name" "$w_desc"
 
  done <<< "$filtered_data"
 
  $found || log::wg_warning "No hosts configured."
  echo ""
}

function cmd::hosts::_render_table() {
  local data="${1:-}"
  [[ -z "$data" ]] && return 0
 
  ui::hosts::list_header_table
  while IFS='|' read -r type key name desc tags; do
    [[ -z "$type" ]] && continue
    ui::hosts::list_row_table "$type" "$key" "$name" "$desc" "$tags"
  done <<< "$data"
}

# ============================================
# Show
# ============================================

function cmd::hosts::show() {
  local ip="" subnet="" port=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --ip)     ip="$2";     shift 2 ;;
      --subnet) subnet="$2"; shift 2 ;;
      --port)   port="$2";   shift 2 ;;
      --help)   cmd::hosts::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  local key entry_type
  if [[ -n "$ip" ]];     then key="$ip";     entry_type="host";   fi
  if [[ -n "$subnet" ]]; then key="$subnet";  entry_type="subnet"; fi
  if [[ -n "$port" ]];   then key="$port";    entry_type="port";   fi

  [[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1

  hosts::require_exists "$entry_type" "$key" || return 1

  log::section "${entry_type^}: ${key}"
  printf "\n"

  while IFS='|' read -r field val; do
    case "$field" in
      name) ui::row "Name"        "${val:-—}" ;;
      desc) ui::row "Description" "${val:-—}" ;;
      tags) ui::row "Tags"        "${val:-—}" ;;
    esac
  done < <(json::hosts_show "$(ctx::hosts)" "$key" "$entry_type")

  printf "\n"
}

# ============================================
# Add
# ============================================

function cmd::hosts::add() {
  local ip="" subnet="" port="" name="" desc="" tags=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --ip)     ip="$2";     shift 2 ;;
      --subnet) subnet="$2"; shift 2 ;;
      --port)   port="$2";   shift 2 ;;
      --name)   name="$2";   shift 2 ;;
      --desc)   desc="$2";   shift 2 ;;
      --tag)    tags+=("$2"); shift 2 ;;
      --tags)   IFS=',' read -ra t <<< "$2"; tags+=("${t[@]}"); shift 2 ;;
      --help)   cmd::hosts::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1

  local key entry_type
  if [[ -n "$ip" ]];     then key="$ip";     entry_type="host";   fi
  if [[ -n "$subnet" ]]; then key="$subnet";  entry_type="subnet"; fi
  if [[ -n "$port" ]];   then key="$port";    entry_type="port";   fi

  [[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1

  local tags_str
  tags_str=$(IFS=','; echo "${tags[*]}")

  json::hosts_add "$(ctx::hosts)" "$entry_type" "$key" "$name" "$desc" "$tags_str"
  log::wg_success "Added ${entry_type}: ${key} → ${name}"
}

# ============================================
# Remove
# ============================================

function cmd::hosts::rm() {
  local ip="" subnet="" port="" force=false

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --ip)     ip="$2";     shift 2 ;;
      --subnet) subnet="$2"; shift 2 ;;
      --port)   port="$2";   shift 2 ;;
      --force)  force=true;  shift   ;;
      --help)   cmd::hosts::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  local key entry_type
  if [[ -n "$ip" ]];     then key="$ip";     entry_type="host";   fi
  if [[ -n "$subnet" ]]; then key="$subnet";  entry_type="subnet"; fi
  if [[ -n "$port" ]];   then key="$port";    entry_type="port";   fi

  [[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1

  hosts::require_exists "$entry_type" "$key" || return 1

  if ! $force; then
    read -r -p "Remove ${entry_type} '${key}'? [y/N] " confirm
    case "$confirm" in
      [yY]*) ;;
      *) log::info "Aborted"; return 0 ;;
    esac
  fi

  json::hosts_remove "$(ctx::hosts)" "$entry_type" "$key"
  log::wg_success "Removed ${entry_type}: ${key}"
}

function cmd::hosts::_output_json() {
  local data
  data=$(json::hosts_list "$(ctx::hosts)" 2>/dev/null)
 
  local -a hosts=()
  while IFS='|' read -r type ip name desc tags; do
    [[ -z "$type" ]] && continue
 
    local tags_json="[]"
    if [[ -n "$tags" ]]; then
      local tags_array
      tags_array=$(echo "$tags" | tr ',' '\n' | \
        while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//')
      tags_json="[${tags_array}]"
    fi
 
    hosts+=("$(printf '{"type":"%s","ip":"%s","name":"%s","desc":"%s","tags":%s}' \
      "$type" "$ip" "$name" "$desc" "$tags_json")")
  done <<< "$data"
 
  local count=${#hosts[@]}
  local array
  array=$(printf '%s\n' "${hosts[@]:-}" | paste -sd ',' -)
  printf '{"hosts":[%s]}' "${array:-}" | json::envelope "hosts list" "$count"
}