#!/usr/bin/env bash

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

function cmd::watch::on_load() {
  flag::register --type
  flag::register --name
  flag::register --peers
  flag::register --blocked
  flag::register --restricted
  flag::register --allowed
  flag::register --raw
}

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

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

Live monitor of WireGuard activity.
  - Handshakes from connected peers (green)
  - Connection attempts from blocked peers (red)
  - Firewall drops from restricted peers (red)

Options:
  --name <name>     Filter by client name
  --type <type>     Filter by device type
  --blocked         Show only blocked peer attempts
  --allowed         Show only handshakes
  --restricted      Show only firewall drop events
  --raw             Show raw IPs without host/service resolution

Examples:
  wgctl watch
  wgctl watch --name phone-nuno
  wgctl watch --blocked
  wgctl watch --type phone
EOF
}

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

function cmd::watch::run() {
  local filter_name="" filter_type="" filter_peers=""
  local blocked_only=false allowed_only=false restricted_only=false
  local raw=false

  rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_* 2>/dev/null || true

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)       filter_name="$2";     shift 2 ;;
      --type)       filter_type="$2";     shift 2 ;;
      --peers)      filter_peers="$2";    shift 2 ;;
      --blocked)    blocked_only=true;    shift   ;;
      --allowed)    allowed_only=true;    shift   ;;
      --restricted) restricted_only=true; shift   ;;
      --raw)        _WGCTL_RAW=true;      shift   ;;
      --help)       cmd::watch::help;     return  ;;
      *)
        log::error "Unknown flag: $1"
        cmd::watch::help
        return 1
        ;;
    esac
  done

  log::section "wgctl — Live Monitor (Ctrl+C to stop)"
  printf "\n"

  monitor::live "$filter_name" "$filter_type" "$filter_peers" \
    "$blocked_only" "$restricted_only" "$allowed_only" "$raw"
}

# ============================================
# Handshake Poller
# ============================================

function cmd::watch::_poll_handshakes() {
  local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
  local w_client="${4:-20}" w_dest="${5:-18}"
 
  # Collect rows with sort key before printing
  local -a rows=()
 
  while IFS= read -r line; do
    local public_key ts
    public_key=$(echo "$line" | awk '{print $1}')
    ts=$(echo "$line" | awk '{print $2}')
    [[ -z "$ts" || "$ts" == "0" ]] && continue
 
    local client_name=""
    for conf in "$(ctx::clients)"/*.conf; do
      [[ -f "$conf" ]] || continue
      local cname
      cname=$(basename "$conf" .conf)
      local key
      key=$(keys::public "$cname" 2>/dev/null || echo "")
      if [[ "$key" == "$public_key" ]]; then
        client_name="$cname"
        break
      fi
    done
    [[ -z "$client_name" ]] && continue
    [[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue
 
    local safe_key
    safe_key=$(echo "$public_key" | md5sum | cut -d' ' -f1)
    local prev_ts_file="/tmp/wgctl_hs_${safe_key}"
    local prev_ts="0"
    [[ -f "$prev_ts_file" ]] && prev_ts=$(cat "$prev_ts_file")
    [[ "$ts" == "$prev_ts" ]] && continue
 
    local gap=$(( ts - ${prev_ts:-0} ))
    echo "$ts" > "$prev_ts_file"
    (( gap < ${WG_HANDSHAKE_CHECK_TIME_SEC:-300} )) && continue
 
    local ts_fmt
    ts_fmt=$(fmt::datetime_short "$ts")
 
    # Resolve endpoint — try wg show first, fall back to endpoint cache
    local endpoint
    endpoint=$(monitor::endpoint_for_key "$public_key")

    if [[ -z "$endpoint" ]]; then
      endpoint=$(monitor::get_cached_endpoint "$client_name")
    fi
 
    local ep_raw ep_resolved=""
    ep_raw="${endpoint:-}"
    if [[ -n "$ep_raw" ]]; then
      local ep_parts
      ep_parts=$(resolve::endpoint_parts "$ep_raw")
      ep_resolved="${ep_parts#*|}"
    fi
    row=$(ui::watch::wg_row "$ts_fmt" "$client_name" "$ep_raw" "handshake" \
      "$ep_resolved" "$w_client" "30")
    rows+=("${ts}|${row}")
 
  done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
 
  # Sort by ts descending (most recent first) and print
  if [[ ${#rows[@]} -gt 0 ]]; then
    printf '%s\n' "${rows[@]}" | sort -t'|' -k1,1rn | while IFS= read -r entry; do
      printf "%s\n" "${entry#*|}"
    done
  fi
}
# ============================================
# Event Tailer
# ============================================

function cmd::watch::_tail_events() {
  local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
  local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}"
  local w_client="${7:-20}" w_dest="${8:-18}"
 
  # Build ip->name map
  declare -A ip_to_name=()
  while IFS= read -r conf; do
    local cname
    cname=$(basename "$conf" .conf)
    local ip
    ip=$(grep "^Address" "$conf" 2>/dev/null | awk '{print $3}' | cut -d'/' -f1)
    [[ -n "$ip" && -n "$cname" ]] && ip_to_name["$ip"]="$cname"
  done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
 
  declare -A _WATCH_LAST_FW=()
  declare -A _WATCH_LAST_WG=()
 
  local source_file
  source_file=$(mktemp)
  echo "wg" > "$source_file"
  trap "rm -f '$source_file'" EXIT
 
  tail -f "$(ctx::events_log)" "$(ctx::fw_events_log)" 2>/dev/null \
  | while IFS= read -r line; do
    [[ -z "$line" ]] && continue
 
    if [[ "$line" == "==> "* ]]; then
      [[ "$line" == *"fw_events"* ]] && echo "fw" > "$source_file" || echo "wg" > "$source_file"
      continue
    fi
 
    local source
    source=$(cat "$source_file")
 
    if [[ "$source" == "fw" ]]; then
      $allowed_only && continue
 
      local fw_data
      fw_data=$(python3 "$(ctx::json_helper)" parse_fw_event "$line" 2>/dev/null) || continue
      [[ -z "$fw_data" ]] && continue
 
      local ts src_ip dest_ip dest_port proto
      IFS='|' read -r ts src_ip dest_ip dest_port proto <<< "$fw_data"
      [[ -z "$src_ip" ]] && continue
 
      local client="${ip_to_name[$src_ip]:-$src_ip}"
      [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
 
      local fw_key="${src_ip}:${dest_ip}:${dest_port}:${proto}"
      local now; now=$(date +%s)
      local window=30
      [[ "$proto" == "17" || "$proto" == "udp" ]] && window=10
      [[ "$proto" == "1"  || "$proto" == "icmp" ]] && window=5
      local last="${_WATCH_LAST_FW[$fw_key]:-0}"
      (( now - last < window )) && continue
      _WATCH_LAST_FW["$fw_key"]="$now"
 
      local ts_fmt
      ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
 
      local fw_svc_name
      fw_svc_name=$(resolve::service_name "$dest_ip" "$dest_port" "$proto")
      local fw_src_ep fw_src_resolved=""
      fw_src_ep=$(monitor::get_cached_endpoint "$client")
      if [[ -n "$fw_src_ep" ]]; then
        local fw_ep_parts
        fw_ep_parts=$(resolve::endpoint_parts "$fw_src_ep")
        fw_src_resolved="${fw_ep_parts#*|}"
      fi
      ui::watch::fw_row "$ts_fmt" "$client" "$dest_ip" "$dest_port" "$proto" \
        "$fw_svc_name" "$fw_src_ep" "$fw_src_resolved" \
        "$w_client" "$w_dest" "30"
 
    else
      $restricted_only && continue
 
      local ev_data
      ev_data=$(python3 "$(ctx::json_helper)" parse_event "$line" 2>/dev/null) || continue
      [[ -z "$ev_data" ]] && continue
 
      local ts client endpoint event
      IFS='|' read -r ts client endpoint event <<< "$ev_data"
      [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
      $blocked_only && [[ "$event" != "attempt" ]] && continue
      $allowed_only && [[ "$event" != "handshake" ]] && continue
 
      local wg_key="${client}:${endpoint}:${event}"
      local now; now=$(date +%s)
      local last="${_WATCH_LAST_WG[$wg_key]:-0}"
 
      local window=30
      [[ "$event" == "handshake" ]] && window="${WG_HANDSHAKE_CHECK_TIME_SEC:-300}"
 
      (( now - last < window )) && continue
      _WATCH_LAST_WG["$wg_key"]="$now"
 
      local ts_fmt
      ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
 
      # Resolve endpoint — fall back to endpoint cache if empty
      local wg_ep_raw wg_ep_resolved=""
      wg_ep_raw="${endpoint:-}"
      if [[ -n "$wg_ep_raw" ]]; then
        local wg_ep_parts
        wg_ep_parts=$(resolve::endpoint_parts "$wg_ep_raw")
        wg_ep_resolved="${wg_ep_parts#*|}"
      fi
      ui::watch::wg_row "$ts_fmt" "$client" "$wg_ep_raw" "$event" \
        "$wg_ep_resolved" "$w_client" "30"
    fi
  done
 
  rm -f "$source_file"
}