#!/usr/bin/env bash
# activity.command.sh — WireGuard activity snapshot

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

function cmd::activity::on_load() {
  load_module net

  flag::register --peer
  flag::register --service
  flag::register --ip
  flag::register --hours
  flag::register --type
  flag::register --dropped

  command::mixin json_output
}

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

function cmd::activity::help() {
  cat <<EOF
Usage: wgctl activity [options]

Show WireGuard activity — transfer totals and firewall drops per peer.
Data sources: wg show transfer, fw_events.log

Options:
  --peer <name>       Filter by peer name
  --service <name>    Filter by service (e.g. truenas, proxmox:web-ui)
  --ip <ip>           Filter by destination IP
  --hours <n>         Time window in hours (default: 24, 0 = all time)
  --type <type>       Filter by device type (combined with --peer)
  --dropped           Show only peers with at least one drop

Examples:
  wgctl activity
  wgctl activity --dropped
  wgctl activity --peer phone-nuno
  wgctl activity --service truenas
  wgctl activity --hours 0
  wgctl activity --ip 10.0.0.101
EOF
}

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

function cmd::activity::run() {
  local filter_peer="" filter_service="" filter_ip="" filter_type=""
  local hours=24 dropped_only=false

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --peer)    filter_peer="$2";    shift 2 ;;
      --service) filter_service="$2"; shift 2 ;;
      --ip)      filter_ip="$2";      shift 2 ;;
      --type)    filter_type="$2";    shift 2 ;;
      --hours)   hours="$2";          shift 2 ;;
      --dropped) dropped_only=true;   shift   ;;
      --help)    cmd::activity::help; return  ;;
      *)
        log::error "Unknown flag: $1"
        cmd::activity::help
        return 1
        ;;
    esac
  done

  if command::json; then
    cmd::activity::_output_json "$hours"
    return 0
  fi

  # Resolve peer name if type provided
  if [[ -n "$filter_peer" && -n "$filter_type" ]]; then
    filter_peer=$(peers::resolve_and_require "$filter_peer" "$filter_type") || return 1
  fi

  # Resolve --service to IP
  local service_ip=""
  if [[ -n "$filter_service" ]]; then
    service_ip=$(net::resolve "$filter_service" 2>/dev/null | head -1 | cut -d: -f1) || true
    if [[ -z "$service_ip" ]]; then
      log::error "Service not found: ${filter_service}"
      return 1
    fi
  fi
  [[ -n "$filter_ip" ]] && service_ip="$filter_ip"

  # Fetch aggregated data
  local data
  data=$(json::activity_aggregate \
    "$(ctx::fw_events_log)" \
    "$(ctx::events_log)" \
    "$(config::interface)" \
    "$(ctx::net)" \
    "$(ctx::clients)" \
    "$(ctx::meta)" \
    "$hours" \
    "$filter_peer" \
    "$service_ip" 2>/dev/null)

  if [[ -z "$data" ]]; then
    log::wg_warning "No activity data found"
    return 0
  fi

  # Measure column widths
  local w_peer=16 w_drops=1
  while IFS='|' read -r type rest; do
    case "$type" in
      peer)
        local name drops
        name=$(echo "$rest"  | cut -d'|' -f1)
        drops=$(echo "$rest" | cut -d'|' -f4)
        (( ${#name}  > w_peer  )) && w_peer=${#name}
        (( ${#drops} > w_drops )) && w_drops=${#drops}
        ;;
      service)
        local count
        count=$(echo "$rest" | cut -d'|' -f3)
        (( ${#count} > w_drops )) && w_drops=${#count}
        ;;
    esac
  done <<< "$data"
 
  (( w_peer += 2 ))
 
  # Compute column where drop count starts on peer row:
  # "  " (2) + name (w_peer) + "  ↓" (3) + rx (10) + "  ↑" (3) + tx (10) + "  " (2)
  # ↓ and ↑ are multi-byte (3 bytes, 1 visible) — 2 extra bytes each
  # Visible: 2 + w_peer + 2+1 + 10 + 2+1 + 10 + 2 = w_peer + 30
  local drops_col=$(( w_peer + 30 ))
 
  local hours_display="${hours}h"
  [[ "$hours" == "0" ]] && hours_display="all time"
 
  log::section "Activity Monitor (last ${hours_display})"
  echo ""
  
  if display::is_table "activity"; then
    cmd::activity::_render_table "$data"
    return 0
  fi

  local first_peer=true skip_peer=false
 
  while IFS='|' read -r record_type rest; do
    case "$record_type" in
      peer)
        local name rx tx drops
        IFS='|' read -r name rx tx drops <<< "$rest"
 
        skip_peer=false
        if $dropped_only && [[ "$drops" -eq 0 ]]; then
          skip_peer=true
          continue
        fi
 
        $first_peer || echo ""
        first_peer=false
 
        local rx_fmt tx_fmt
        rx_fmt=$(fmt::bytes "$rx")
        tx_fmt=$(fmt::bytes "$tx")
 
        local name_pad rx_pad tx_pad
        name_pad=$(printf "%-${w_peer}s" "$name")
        rx_pad=$(printf   "%-10s"        "$rx_fmt")
        tx_pad=$(printf   "%-10s"        "$tx_fmt")
 
        local drop_word="drops"
        [[ "$drops" -eq 1 ]] && drop_word="drop"
 
        ui::activity::peer_row \
          "$name_pad" "$rx_pad" "$tx_pad" "$drops" "$drop_word" "$w_drops"
        ;;
 
      service)
        $skip_peer && continue
 
        local peer dest_display drop_count
        IFS='|' read -r peer dest_display drop_count <<< "$rest"
 
        local svc_drop_word="drops"
        [[ "$drop_count" -eq 1 ]] && svc_drop_word="drop"
 
        ui::activity::service_row \
          "$dest_display" "$drop_count" "$svc_drop_word" "$drops_col" "$w_drops"
        ;;
    esac
  done <<< "$data"
 
  echo ""
}

function cmd::activity::_render_table() {
  local data="${1:-}"
  [[ -z "$data" ]] && return 0
 
  ui::activity::header_table
  local skip_peer=false
  while IFS='|' read -r record_type rest; do
    case "$record_type" in
      peer)
        local name rx tx drops
        IFS='|' read -r name rx tx drops <<< "$rest"
        skip_peer=false
        local rx_fmt tx_fmt
        rx_fmt=$(fmt::bytes "$rx")
        tx_fmt=$(fmt::bytes "$tx")
        ui::activity::peer_row_table "$name" "$rx_fmt" "$tx_fmt" "$drops" ""
        ;;
      service)
        $skip_peer && continue
        local peer dest count
        IFS='|' read -r peer dest count <<< "$rest"
        ui::activity::service_row_table "$dest" "$count" "drops"
        ;;
    esac
  done <<< "$data"
}
 

function cmd::activity::_output_json() {
  local hours="${1:-24}"
  local data
  data=$(json::activity_aggregate \
    "$(ctx::fw_events_log)" "$(ctx::events_log)" \
    "$(config::interface)" "$(ctx::net)" \
    "$(ctx::clients)" "$(ctx::meta)" \
    "$hours" "" "" 2>/dev/null)
 
  local -a peers=()
  local current_peer="" current_services=""
  local -a current_svc_list=()
 
  while IFS='|' read -r record_type rest; do
    case "$record_type" in
      peer)
        # Flush previous peer
        if [[ -n "$current_peer" ]]; then
          local svc_array
          svc_array=$(printf '%s\n' "${current_svc_list[@]:-}" | paste -sd ',' -)
          peers+=("${current_peer},\"services\":[${svc_array:-}]}")
          current_svc_list=()
        fi
        local name rx tx drops
        IFS='|' read -r name rx tx drops <<< "$rest"
        current_peer=$(printf '{"name":"%s","rx":%s,"tx":%s,"drops":%s' \
          "$name" "$rx" "$tx" "$drops")
        ;;
      service)
        local peer dest count
        IFS='|' read -r peer dest count <<< "$rest"
        current_svc_list+=("$(printf '{"dest":"%s","drops":%s}' "$dest" "$count")")
        ;;
    esac
  done <<< "$data"
 
  # Flush last peer
  if [[ -n "$current_peer" ]]; then
    local svc_array
    svc_array=$(printf '%s\n' "${current_svc_list[@]:-}" | paste -sd ',' -)
    peers+=("${current_peer},\"services\":[${svc_array:-}]}")
  fi
 
  local count=${#peers[@]}
  local array
  array=$(printf '%s\n' "${peers[@]:-}" | paste -sd ',' -)
  printf '{"peers":[%s]}' "${array:-}" | json::envelope "activity" "$count"
}
