#!/usr/bin/env bash
# peer.command.sh — peer management operations

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

function cmd::peer::on_load() {
  flag::register --name
  flag::register --type
  flag::register --all
  flag::register --mode
  flag::register --dns
  flag::register --fallback-dns
  flag::register --force
}

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

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

Manage peer configuration and settings.

Subcommands:
  update-dns        Update DNS settings in client config(s)
  update-tunnel     Update tunnel mode (split/full) in client config(s)

Options for update-dns:
  --name <name>         Target peer
  --all                 Apply to all peers
  --type <type>         Filter by device type
  --dns <ip>            Primary DNS (default: from config)
  --fallback-dns <ips>  Fallback DNS servers (comma-separated)
                        Default: from WG_DNS_FALLBACK in wgctl.conf

Options for update-tunnel:
  --name <name>         Target peer
  --all                 Apply to all peers
  --type <type>         Filter by device type
  --mode <mode>         Tunnel mode: split | full
  --force               Skip confirmation for --all

Examples:
  wgctl peer update-dns --all
  wgctl peer update-dns --name phone-nuno
  wgctl peer update-dns --name phone-nuno --fallback-dns 9.9.9.9,1.1.1.1
  wgctl peer update-tunnel --all --mode split
  wgctl peer update-tunnel --name phone-nuno --mode full
EOF
}

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

function cmd::peer::run() {
  local subcmd="${1:-help}"
  shift || true
  case "$subcmd" in
    update-dns)    cmd::peer::update_dns    "$@" ;;
    update-tunnel) cmd::peer::update_tunnel "$@" ;;
    help)          cmd::peer::help               ;;
    *)
      log::error "Unknown subcommand: '${subcmd}'"
      cmd::peer::help
      return 1
      ;;
  esac
}

# ============================================
# Update DNS
# ============================================

function cmd::peer::update_dns() {
  local name="" type="" all=false
  local dns="" fallback_dns=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)         name="$2";         shift 2 ;;
      --type)         type="$2";         shift 2 ;;
      --all)          all=true;          shift   ;;
      --dns)          dns="$2";          shift 2 ;;
      --fallback-dns) fallback_dns="$2"; shift 2 ;;
      --help)         cmd::peer::help;   return  ;;
      *) log::error "Unknown flag: $1";  return 1 ;;
    esac
  done

  [[ -z "$name" && "$all" == "false" ]] && \
    log::error "Specify --name or --all" && return 1

  # Resolve DNS string
  local primary="${dns:-$(config::dns)}"
  local fallback="${fallback_dns:-$(config::dns_fallback)}"
  local dns_string
  if [[ -n "$fallback" ]]; then
    dns_string="${primary}, ${fallback}"
  else
    dns_string="$primary"
  fi

  # Collect target peers
  local peers=()
  if $all; then
    while IFS= read -r conf; do
      peers+=("$(basename "$conf" .conf)")
    done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
  else
    name=$(peers::resolve_and_require "$name" "$type") || return 1
    peers=("$name")
  fi

  local updated=0
  for peer_name in "${peers[@]}"; do
    local conf
    conf="$(ctx::clients)/${peer_name}.conf"
    [[ ! -f "$conf" ]] && continue

    # Replace DNS line in-place
    if grep -q "^DNS" "$conf"; then
      sed -i "s|^DNS = .*|DNS = ${dns_string}|" "$conf"
    else
      # Add DNS line after Address line
      sed -i "/^Address/a DNS = ${dns_string}" "$conf"
    fi
    (( updated++ )) || true
    log::debug "Updated DNS for: ${peer_name}"
  done

  log::wg_success "Updated DNS to '${dns_string}' for ${updated} peer(s)"
}

# ============================================
# Update Tunnel
# ============================================

function cmd::peer::update_tunnel() {
  local name="" type="" all=false mode="" force=false

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)  name="$2";        shift 2 ;;
      --type)  type="$2";        shift 2 ;;
      --all)   all=true;         shift   ;;
      --mode)  mode="$2";        shift 2 ;;
      --force) force=true;       shift   ;;
      --help)  cmd::peer::help;  return  ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" && "$all" == "false" ]] && \
    log::error "Specify --name or --all" && return 1
  [[ -z "$mode" ]] && \
    log::error "Missing required flag: --mode (split|full)" && return 1
  [[ "$mode" != "split" && "$mode" != "full" ]] && \
    log::error "Invalid mode: ${mode} (must be split or full)" && return 1

  local allowed_ips
  allowed_ips=$(config::allowed_ips_for "$mode")

  # Collect target peers
  local peers=()
  if $all; then
    if ! $force; then
      read -r -p "Update tunnel mode to '${mode}' for ALL peers? [y/N] " confirm
      case "$confirm" in [yY]*) ;; *) log::info "Aborted"; return 0 ;; esac
    fi
    while IFS= read -r conf; do
      peers+=("$(basename "$conf" .conf)")
    done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
  else
    name=$(peers::resolve_and_require "$name" "$type") || return 1
    peers=("$name")
  fi

  local updated=0
  for peer_name in "${peers[@]}"; do
    local conf
    conf="$(ctx::clients)/${peer_name}.conf"
    [[ ! -f "$conf" ]] && continue

    # Replace AllowedIPs line in-place
    sed -i "s|^AllowedIPs = .*|AllowedIPs = ${allowed_ips}|" "$conf"
    (( updated++ )) || true
    log::debug "Updated tunnel for: ${peer_name}"
  done

  log::wg_success "Updated tunnel to '${mode}' (${allowed_ips}) for ${updated} peer(s)"
  log::wg "Peers must reconnect to apply the new tunnel mode"
}