#!/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
  flag::register --all
  flag::register --before
  flag::register --force
  flag::register --days
}

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

Show or manage WireGuard and firewall activity logs.

Subcommands:
  show (default)    Show activity logs
  remove, rm        Remove log entries
  rotate            Remove entries older than N days

Options for show:
  --name <name>     Filter by client name
  --type <type>     Filter by device type
  --limit <n>       Max results per source (default: 50)
  --fw              Show only firewall drops
  --wg              Show only WireGuard events
  --follow, -f      Follow logs in real time

Options for remove:
  --name <name>     Remove entries for specific peer
  --all             Remove all log entries
  --fw              Remove only firewall events
  --wg              Remove only WireGuard events
  --before <days>   Remove entries older than N days
  --force           Skip confirmation

Options for rotate:
  --days <n>        Days to keep (default: 7)
  --force           Skip confirmation

Examples:
  wgctl logs
  wgctl logs --name phone-nuno
  wgctl logs --fw --limit 100
  wgctl logs --follow
  wgctl logs remove --name phone-nuno
  wgctl logs remove --all --force
  wgctl logs remove --fw --before 1
  wgctl logs rotate
  wgctl logs rotate --days 30 --force
EOF
}

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

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

function cmd::logs::show() {
  local name="" type="" 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 ;;
      --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="" before="" force=false
  local fw_only=false wg_only=false all=false

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

  # Validate — need at least one filter
  if ! $all && [[ -z "$name" && -z "$before" ]]; then
    log::error "Specify --name, --before, or --all"
    cmd::logs::help
    return 1
  fi

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

  # Build description for confirmation
  local desc=""
  $all    && desc="all entries"
  [[ -n "$name"   ]] && desc="entries for '${name}'"
  [[ -n "$before" ]] && desc="${desc:+$desc, }entries older than ${before} days"
  $fw_only && desc="${desc} (fw only)"
  $wg_only && desc="${desc} (wg only)"

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

  local result
  result=$(json::remove_events_filtered \
    "$WG_EVENTS_LOG" "$FW_EVENTS_LOG" \
    "${name:-}" "${filter_ip:-}" \
    "$fw_only" "$wg_only" \
    "${before:-}")

  local removed_wg removed_fw
  IFS="|" read -r removed_wg removed_fw <<< "$result"
  local total=$(( removed_wg + removed_fw ))

  if [[ "$total" -eq 0 ]]; then
    log::wg_warning "No log entries found matching the criteria"
    return 0
  fi

  log::wg_success "Removed ${total} log entries (wg: ${removed_wg}, fw: ${removed_fw})"
}

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

  [[ ! -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;31m${event}\033[0m" ;;
      handshake*) colored_event="\033[1;32m${event}\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:-50}"

  [[ ! -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;33m${proto}\033[0m" ;;
      udp*)  colored_proto="\033[1;36m${proto}\033[0m" ;;
      icmp*) colored_proto="\033[0;37m${proto}\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"
}



function cmd::logs::rotate() {
  local days=7 force=false

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

  $force || {
    read -r -p "Remove log entries older than ${days} days? [y/N] " confirm
    case "$confirm" in
      [yY]*) ;;
      *) log::info "Aborted"; return 0 ;;
    esac
  }

  local result
  result=$(json::remove_events_filtered \
    "$WG_EVENTS_LOG" "$FW_EVENTS_LOG" \
    "" "" "false" "false" "$days")

  local removed_wg removed_fw
  IFS="|" read -r removed_wg removed_fw <<< "$result"
  local total=$(( removed_wg + removed_fw ))

  if [[ "$total" -eq 0 ]]; then
    log::wg_warning "No log entries older than ${days} days"
    return 0
  fi

  log::wg_success "Rotated ${total} entries older than ${days} days (wg: ${removed_wg}, fw: ${removed_fw})"
}