#!/usr/bin/env bash

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

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

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

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

Live monitor of WireGuard activity.
Shows handshakes from active clients and connection attempts from blocked clients.

Options:
  --type <type>     Filter by device type
  --name <name>     Filter by client name
  --allowed         Show only allowed client handshakes
  --restricted      Show only restricted client events
  --blocked         Show only blocked client attempts

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

# ============================================
# Helpers
# ============================================

function cmd::watch::format_event() {
  local ts="$1"
  local client="$2"
  local endpoint="$3"
  local event="$4"
  local status="$5"

  local event_color
  case "$event" in
    attempt)   event_color="\033[1;31m" ;;  # red
    handshake) event_color="\033[1;32m" ;;  # green
    *)         event_color="\033[0;37m" ;;  # grey
  esac

  local status_str=""
  if [[ -n "$status" ]]; then
    case "$status" in
      blocked) status_str=" \033[1;31mblocked\033[0m" ;;
      allowed) status_str=" \033[1;32mallowed\033[0m" ;;
      *)       status_str="" ;;
    esac
  fi

  printf "  %-20s %-25s %-20s ${event_color}%-12s\033[0m%b\n" \
    "$ts" "$client" "${endpoint:-—}" "$event" "$status_str"
}

function cmd::watch::header() {
  log::section "wgctl — Live Monitor (Ctrl+C to stop)"
  printf "\n  %-20s %-25s %-20s %-12s %s\n" \
    "TIME" "CLIENT" "ENDPOINT" "EVENT" "STATUS"
  printf "  %s\n\n" "$(printf '─%.0s' {1..85})"
}

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

declare -A _WATCH_LAST_HANDSHAKES=()

function cmd::watch::poll_handshakes() {
  local filter_name="$1"
  local filter_type="$2"
  local allowed_only="$3"

  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

    # Find client by public key
    local client_name=""
    for conf in "$(ctx::clients)"/*.conf; do
      [[ -f "$conf" ]] || continue
      local name
      name=$(basename "$conf" .conf)
      local key
      key=$(keys::public "$name" 2>/dev/null || echo "")
      if [[ "$key" == "$public_key" ]]; then
        client_name="$name"
        break
      fi
    done

    [[ -z "$client_name" ]] && continue

    # Apply filters
    [[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue

    if [[ -n "$filter_type" ]]; then
      local ip
      ip=$(grep "^Address" "$(ctx::clients)/${client_name}.conf" | awk '{print $3}' | cut -d'/' -f1)
      local subnet
      subnet=$(config::subnet_for "$filter_type")
      string::starts_with "$ip" "$subnet" || continue
    fi

    # Only emit if handshake is new
    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")
    if [[ "$ts" != "$prev_ts" ]]; then
      echo "$ts" > "$prev_ts_file"

      local formatted_ts
      formatted_ts=$(fmt::datetime "$ts")

      local endpoint
      endpoint=$(monitor::endpoint_for_key "$public_key")

      cmd::watch::format_event \
        "$formatted_ts" "$client_name" "${endpoint:-—}" "handshake" "allowed"
    fi

  done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
}

# ============================================
# Event Tailer
# ============================================

function cmd::watch::tail_events() {
  local filter_name="$1"
  local filter_type="$2"
  local blocked_only="$3"
  local restricted_only="$4"
  local allowed_only="$5"

  declare -A _WATCH_LAST_ATTEMPT=()

  tail -f "$(ctx::root)/daemon/events.log" 2>/dev/null | while IFS= read -r line; do
  [[ -z "$line" ]] && continue

  local event_data
  event_data=$(json::parse_event "$line")

  [[ -z "$event_data" ]] && continue

  local ts client endpoint event
  IFS="|" read -r ts client endpoint event <<< "$event_data"

  if $restricted_only; then
    local conf
    conf="$(ctx::clients)/${client}.conf"
    [[ -f "$conf" ]] || continue
    cmd::list::is_restricted "$client" || continue
  fi

  # Apply filters
  [[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue

  if [[ -n "$filter_type" ]]; then
    local conf
    conf="$(ctx::clients)/${client}.conf"
    [[ -f "$conf" ]] || continue
    local ip
    ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
    local subnet
    subnet=$(config::subnet_for "$filter_type")
    string::starts_with "$ip" "$subnet" || continue
  fi

  # Filter by status
  if $allowed_only && [[ "$event" != "handshake" ]]; then
    continue
  fi

  if $restricted_only; then
    local conf
    conf="$(ctx::clients)/${client}.conf"
    [[ -f "$conf" ]] || continue
    cmd::list::is_restricted "$client" || continue
  fi

  local formatted_ts
  formatted_ts=$(fmt::datetime_iso "$ts")

  # Before printing the event
  local now
  now=$(date +%s)
  local safe_client="${client//[-.]/_}"
  local last="${_WATCH_LAST_ATTEMPT[$safe_client]:-0}"
  local diff=$(( now - last ))
  if (( diff < 30 )); then
    continue
  fi
  _WATCH_LAST_ATTEMPT[$safe_client]="$now"

  cmd::watch::format_event \
    "$formatted_ts" "$client" "${endpoint:-—}" "$event" "blocked"
  done
}

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

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

  # Clean up any stale temp files from previous runs
  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 ;;
      --blocked)    blocked_only=true;    shift   ;;
      --allowed)    allowed_only=true;    shift   ;;
      --restricted) restricted_only=true; shift   ;;
      --help)       cmd::watch::help;     return  ;;
      *)
        log::error "Unknown flag: $1"
        cmd::watch::help
        return 1
        ;;
    esac
  done

  cmd::watch::header

  # Start event tailer in background unless --blocked not set
  if ! $blocked_only && ! $restricted_only; then
    # Poll handshakes every 5 seconds in background
    (
      while true; do
        cmd::watch::poll_handshakes "$filter_name" "$filter_type" "$allowed_only"
        sleep 5
      done
    ) &
    local poller_pid=$!
  fi

  # Tail events log for blocked attempts
  cmd::watch::tail_events "$filter_name" "$filter_type" "$blocked_only" "$restricted_only" "$allowed_only" &
  local tailer_pid=$!

  # Trap Ctrl+C to clean up background processes
  trap "kill $tailer_pid ${poller_pid:-} 2>/dev/null; rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_*; echo ''; exit 0" INT TERM

  # Keep main process alive
  wait
}
