#!/usr/bin/env bash

# ============================================
# Config
# ============================================

MONITOR_DIR="$(ctx::daemon)"
WATCHLIST_FILE="${MONITOR_DIR}/watchlist.json"
EVENTS_LOG="${MONITOR_DIR}/events.log"
ENDPOINT_CACHE="${MONITOR_DIR}/endpoint_cache.json"
FW_EVENTS_LOG="${MONITOR_DIR}/fw_events.log"
MONITOR_SERVICE="wgctl-monitor"

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

function monitor::on_load() {
  if [[ ! -f "$WATCHLIST_FILE" ]]; then
    echo '{}' > "$WATCHLIST_FILE"
  fi
  if [[ ! -f "$EVENTS_LOG" ]]; then
    touch "$EVENTS_LOG"
  fi
}

# ============================================
# Watchlist
# ============================================

function monitor::watch() {
  local ip="$1" client="$2"
  json::set "$WATCHLIST_FILE" "$ip" "$client"
  log::debug "Watching: ${client} (${ip})"
}

function monitor::unwatch() {
  local ip="$1"
  json::delete "$WATCHLIST_FILE" "$ip"
}

function monitor::is_watched() {
  local ip="$1"
  json::has_key "$WATCHLIST_FILE" "$ip"
}

function monitor::unwatch_client() {
  local name="$1"
  json::filter_values "$WATCHLIST_FILE" "value" "$name"
  log::debug "Unwatched client: ${name}"
}

# ============================================
# Events
# ============================================

function monitor::last_attempt() {
  local client="$1"
  json::last_event "$EVENTS_LOG" "client" "timestamp" "$client"
}

function monitor::last_endpoint() {
  local client="$1"
  json::last_event "$EVENTS_LOG" "client" "endpoint" "$client"
}

function monitor::events_for() {
  local ip="$1"
  local limit="${2:-50}"
  json::events_for "$EVENTS_LOG" "$ip" "$limit"
}

function monitor::get_handshake_ts() {
  local public_key="${1:-}"
  [[ -z "$public_key" ]] && echo "0" && return
  wg show "$(config::interface)" latest-handshakes 2>/dev/null \
    | grep "^${public_key}" | awk '{print $2}' || echo "0"
}

# ============================================
# Endpoint Cache (for blocked clients)
# ============================================

ENDPOINT_CACHE="${WGCTL_DIR}/daemon/endpoint_cache.json"

function monitor::cache_endpoint() {
  local client="$1" ip="$2"
  json::set "$ENDPOINT_CACHE" "$client" "$ip"
}

function monitor::get_cached_endpoint() {
  local client="${1:-}"
  json::get "$ENDPOINT_CACHE" "$client"
}

function monitor::update_endpoint_cache() {
  while IFS=$'\t' read -r key endpoint; do
    [[ "$endpoint" == "(none)" ]] && continue
    local ip
    ip=$(echo "$endpoint" | cut -d':' -f1)
    local client
    client=$(keys::find_by_public "$key") || continue
    monitor::cache_endpoint "$client" "$ip"
  done < <(wg show "$(config::interface)" endpoints 2>/dev/null)
}

# ============================================
# Endpoint (from wg show, for active clients)
# ============================================

function monitor::endpoint_for_key() {
  local public_key="$1"
  wg show "$(config::interface)" endpoints 2>/dev/null \
    | grep "^${public_key}" \
    | awk '{print $2}' \
    | cut -d':' -f1
}

# ============================================
# Service
# ============================================

function monitor::start() {
  systemctl start "$MONITOR_SERVICE"
  log::debug "Monitor daemon started"
}

function monitor::stop() {
  systemctl stop "$MONITOR_SERVICE"
  log::debug "Monitor daemon stopped"
}

function monitor::restart() {
  systemctl restart "$MONITOR_SERVICE"
  log::debug "Monitor daemon restarted"
}

function monitor::is_running() {
  systemctl is-active --quiet "$MONITOR_SERVICE"
}

function monitor::live() {
  local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
  local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}"
  local raw="${7:-false}"

  [[ "$raw" == "true" ]] && _WGCTL_RAW=true

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

  local w_client=20 w_dest=18
  while IFS= read -r conf; do
    local name
    name=$(basename "$conf" .conf)
    (( ${#name} > w_client )) && w_client=${#name}
  done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
  (( w_client += 2 ))

  if ! $blocked_only && ! $restricted_only; then
    (
      while true; do
        cmd::watch::_poll_handshakes \
          "$filter_name" "$filter_type" "$filter_peers" "$w_client" "$w_dest"
        sleep 5
      done
    ) &
    local poller_pid=$!
  fi

  cmd::watch::_tail_events \
    "$filter_name" "$filter_type" "$filter_peers" \
    "$blocked_only" "$restricted_only" "$allowed_only" \
    "$w_client" "$w_dest" &
  local tailer_pid=$!

  trap "kill $tailer_pid ${poller_pid:-} 2>/dev/null; \
    rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_*; printf '\n'; exit 0" INT TERM

  wait
}
