#!/usr/bin/env bash

FW_EVENTS_LOG="$(ctx::fw_events_log)"
WG_EVENTS_LOG="$(ctx::events_log)"

function cmd::logs::on_load() {
  flag::register --name
  flag::register --type
  flag::register --since
  flag::register --limit
  flag::register --fw
  flag::register --wg
  flag::register --follow
}

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

Show WireGuard and firewall activity logs.

Options:
  --name <name>     Filter by client name
  --type <type>     Filter by device type
  --since <time>    Time filter (e.g. 1h, 24h, 7d)
  --limit <n>       Max results per source (default 50)
  --fw              Show only firewall drops
  --wg              Show only WireGuard events

Examples:
  wgctl logs
  wgctl logs --name guest-test
  wgctl logs --type guest
  wgctl logs --since 1h
  wgctl logs --fw --limit 100
EOF
}

function cmd::logs::run() {
  local subcmd="${1:-show}"
  
  # Check if first arg is a flag
  if [[ "$subcmd" == --* ]]; then
    subcmd="show"
  else
    shift || true
  fi

  case "$subcmd" in
    show)            cmd::logs::show "$@"   ;;
    remove|rm|del)   cmd::logs::remove "$@" ;;
    help)            cmd::logs::help        ;;
    *)
      log::error "Unknown subcommand: '${subcmd}'"
      cmd::logs::help
      return 1
      ;;
  esac
}

function cmd::logs::show() {
  local name="" type="" since="" limit=50
  local fw_only=false wg_only=false follow=false

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)    name="$2";      shift 2 ;;
      --type)    type="$2";      shift 2 ;;
      --since)   since="$2";     shift 2 ;;
      --limit)   limit="$2";     shift 2 ;;
      --fw)      fw_only=true;   shift   ;;
      --wg)      wg_only=true;   shift   ;;
      --follow|-f) follow=true;  shift   ;;
      --help)    cmd::logs::help; return ;;
      *)
        log::error "Unknown flag: $1"
        return 1
        ;;
    esac
  done

  if [[ -n "$name" && -n "$type" ]]; then
    name=$(peers::resolve_and_require "$name" "$type") || return 1
  fi

  local filter_ip=""
  if [[ -n "$name" ]]; then
    filter_ip=$(peers::get_ip "$name")
    [[ -z "$filter_ip" ]] && log::error "Could not find IP for: $name" && return 1
  fi

  if $follow; then
    cmd::logs::follow "$filter_ip" "$name" "$type" "$fw_only" "$wg_only"
    return
  fi

  log::section "WireGuard Activity Log"
  printf "\n"
  $wg_only || cmd::logs::show_fw_events "$filter_ip" "$name" "$type" "$limit"
  $fw_only || cmd::logs::show_wg_events "$filter_ip" "$name" "$type" "$limit"
}

function cmd::logs::follow() {
  local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}"
  local fw_only="${4:-false}" wg_only="${5:-false}"
  local filter_peers="${6:-}"

  local clients_dir
  clients_dir="$(ctx::clients)"

  local wg_log="$WG_EVENTS_LOG"
  local fw_log="$FW_EVENTS_LOG"
  $fw_only && wg_log=""
  $wg_only && fw_log=""

  log::section "WireGuard Live Log (Ctrl+C to stop)"
  printf "\n  %-20s %-8s %-20s %-25s %s\n" \
    "TIME" "SOURCE" "CLIENT" "DESTINATION/ENDPOINT" "EVENT"
  printf "  %s\n" "$(printf '─%.0s' {1..90})"

  while IFS="|" read -r source ts client dst_or_endpoint event; do
    if [[ "$source" == "fw" ]]; then
      local colored_event
      case "$event" in
        tcp)  colored_event="\033[1;33mtcp\033[0m"  ;;
        udp)  colored_event="\033[0;36mudp\033[0m"  ;;
        icmp) colored_event="\033[0;37micmp\033[0m" ;;
        *)    colored_event="$event" ;;
      esac
      printf "  %-20s %-8s %-20s %-25s %b\n" \
        "$ts" "firewall" "$client" "$dst_or_endpoint" "$colored_event"
    else
      local colored_event
      case "$event" in
        attempt)   colored_event="\033[1;31mattempt\033[0m"   ;;
        handshake) colored_event="\033[1;32mhandshake\033[0m" ;;
        *)         colored_event="$event" ;;
      esac
      printf "  %-20s %-8s %-20s %-25s %b\n" \
        "$ts" "wireguard" "$client" "$dst_or_endpoint" "$colored_event"
    fi
  done < <(json::follow_logs "$fw_log" "$wg_log" "$filter_ip" "$filter_type" "$clients_dir" "$filter_peers")
}

function cmd::logs::remove() {
  local name="" type="" force=false

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

  if [[ -z "$name" ]]; then
    log::error "Missing required flag: --name"
    return 1
  fi

  name=$(peers::resolve_and_require "$name" "$type") || return 1

  local client_ip
  client_ip=$(peers::get_ip "$name")

  local before_wg before_fw after_wg after_fw
  before_wg=$(wc -l < "$WG_EVENTS_LOG" 2>/dev/null || echo 0)
  before_fw=$(wc -l < "$FW_EVENTS_LOG" 2>/dev/null || echo 0)

  json::remove_events "$WG_EVENTS_LOG" "$name"
  json::remove_events "$FW_EVENTS_LOG" "$client_ip"

  after_wg=$(wc -l < "$WG_EVENTS_LOG" 2>/dev/null || echo 0)
  after_fw=$(wc -l < "$FW_EVENTS_LOG" 2>/dev/null || echo 0)

  local removed=$(( (before_wg - after_wg) + (before_fw - after_fw) ))
  if [[ "$removed" -eq 0 ]]; then
    log::wg_warning "No log entries found for: ${name}"
    return 0
  fi

  if ! $force; then
    read -r -p "Remove all log entries for '${name}'? [y/N] " confirm
    case "$confirm" in
      [yY][eE][sS]|[yY]) ;;
      *) log::info "Aborted"; return 0 ;;
    esac
  fi

  log::wg_success "Removed ${removed} log entries for: ${name}"

  json::remove_events "$WG_EVENTS_LOG" "$name"
  json::remove_events "$FW_EVENTS_LOG" "$client_ip"

  log::wg_success "Removed log entries for: ${name}"
}

function cmd::logs::show_wg_events() {
  local filter_ip="$1" filter_name="$2" filter_type="$3" limit="$4"

  [[ ! -f "$WG_EVENTS_LOG" ]] && return 0

  printf "  WireGuard Events:\n"
  printf "  %-20s %-20s %-18s %s\n" "TIME" "CLIENT" "ENDPOINT" "EVENT"
  printf "  %s\n" "$(printf '─%.0s' {1..75})"

  local found=false
  while IFS="|" read -r ts client endpoint event; do
    [[ -z "$ts" ]] && continue
    local colored_event
    case "$event" in
      attempt)   colored_event="\033[1;31mattempt\033[0m"   ;;
      handshake) colored_event="\033[1;32mhandshake\033[0m" ;;
      *)         colored_event="$event" ;;
    esac
    printf "  %-20s %-20s %-18s %b\n" "$ts" "$client" "$endpoint" "$colored_event"
    found=true
  done < <(json::wg_events "$WG_EVENTS_LOG" "$filter_name" "$filter_type" "$limit")

  $found || printf "  —\n"
  printf "\n"
}

function cmd::logs::show_fw_events() {
  local filter_ip="$1" filter_name="$2" filter_type="$3" limit="$4"

  [[ ! -f "$FW_EVENTS_LOG" ]] && return 0

  printf "  Firewall Drops:\n"
  printf "  %-20s %-18s %-25s %s\n" "TIME" "CLIENT" "DESTINATION" "PROTOCOL"
  printf "  %s\n" "$(printf '─%.0s' {1..75})"

  local found=false
  while IFS="|" read -r ts client dst proto; do
    [[ -z "$ts" ]] && continue
    local colored_proto
    case "$proto" in
      tcp)  colored_proto="\033[1;33mtcp\033[0m" ;;
      udp)  colored_proto="\033[1;36mudp\033[0m" ;;
      icmp) colored_proto="\033[0;37micmp\033[0m" ;;
      *)    colored_proto="$proto" ;;
    esac
    printf "  %-20s %-18s %-25s %b\n" "$ts" "$client" "$dst" "$colored_proto"
    found=true
  done < <(json::fw_events "$FW_EVENTS_LOG" "$filter_ip" "$filter_type" "$(ctx::clients)" "$limit")

  $found || printf "  —\n"
  printf "\n"
}