#!/usr/bin/env bash
# commands/group/helpers.sh
# All logs implementation logic — called by subcommand run functions

function cmd::logs::show_fw_events() {
  local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \
        limit="${4:-50}" net_file="${5:-}" collapse="${6:-1}" \
        since="${7:-}" filter_dest_ip="${8:-}" filter_dest_port="${9:-}" \
        sort_order="${10:-desc}" resolved_only="${11:-false}"
 
  [[ ! -f "$FW_EVENTS_LOG" ]] && return 0
 
  local data
  data=$(json::fw_events \
    "$FW_EVENTS_LOG" "$filter_ip" "$filter_type" \
    "$(ctx::clients)" "${net_file:-}" \
    "$limit" "$collapse" "$since" \
    "$filter_dest_ip" "$filter_dest_port" \
    "$sort_order" \
    2>/dev/null)
 
  [[ -z "$data" ]] && return 0
 
  # ── Collect unique endpoints for batch resolution ──
  local -a ep_list=()
  while IFS='|' read -r ts client dest_ip dest_port proto svc count src_endpoint; do
    [[ -z "$ts" || -z "$src_endpoint" ]] && continue
    ep_list+=("$src_endpoint")
  done <<< "$data"
 
  declare -A resolve_cache=()
  if [[ ${#ep_list[@]} -gt 0 ]]; then
    while IFS='|' read -r ip name; do
      [[ -n "$ip" ]] && resolve_cache["$ip"]="$name"
    done < <(json::batch_resolve "${ep_list[@]}" 2>/dev/null)
  fi
 
  # ── Pass 1: measure widths ──
  local w_client=16 w_dest=20 w_endpoint=0
  local resolved_data=""
 
  while IFS='|' read -r ts client dest_ip dest_port proto svc count src_endpoint; do
    [[ -z "$ts" ]] && continue
 
    (( ${#client} > w_client )) && w_client=${#client}
 
    local svc_display=""
    if [[ -n "$svc" ]]; then
      [[ -n "$dest_port" ]] && svc_display="${svc}/${proto}" \
                            || svc_display="${svc} (${proto})"
    else
      [[ -n "$dest_port" ]] && svc_display="${dest_ip}:${dest_port}/${proto}" \
                            || svc_display="${dest_ip} (${proto})"
    fi
 
    local measure_len
    if $resolved_only; then
      measure_len=${#svc_display}
    else
      local raw_plain=""
      [[ -n "$svc" && -n "$dest_port" ]] && raw_plain=" (${dest_ip}:${dest_port})"
      [[ -n "$svc" && -z "$dest_port" ]] && raw_plain=" (${dest_ip})"
      measure_len=$(( ${#svc_display} + ${#raw_plain} ))
    fi
    (( measure_len > w_dest )) && w_dest=$measure_len
 
    local src_resolved=""
    if [[ -n "$src_endpoint" ]]; then
      src_resolved="${resolve_cache[$src_endpoint]:-}"
      [[ "$src_resolved" == "$src_endpoint" ]] && src_resolved=""
 
      local ep_measure_len
      if $resolved_only; then
        ep_measure_len=${#src_resolved}
        [[ -z "$src_resolved" ]] && ep_measure_len=${#src_endpoint}
      else
        ep_measure_len=${#src_endpoint}
        [[ -n "$src_resolved" ]] && \
          ep_measure_len=$(( ${#src_endpoint} + 4 + ${#src_resolved} ))
      fi
      (( ep_measure_len > w_endpoint )) && w_endpoint=$ep_measure_len
    fi
 
    resolved_data+="${ts}|${client}|${dest_ip}|${dest_port}|${proto}|${svc}|${count}|${src_endpoint}|${src_resolved}"$'\n'
  done <<< "$data"
 
  (( w_client += 2 ))
  (( w_dest   += 2 ))
  [[ "$w_endpoint" -gt 0 ]] && (( w_endpoint += 2 ))
 
  # ── Pass 2: render ──
  ui::logs::fw_section_header
  while IFS='|' read -r ts client dest_ip dest_port proto svc count src_endpoint src_resolved; do
    [[ -z "$ts" ]] && continue
    ui::logs::fw_row "$ts" "$client" "$dest_ip" "$dest_port" \
      "$proto" "$svc" "$count" "$w_client" "$w_dest" \
      "$src_endpoint" "$src_resolved" "$w_endpoint" "$resolved_only"
  done <<< "$resolved_data"
  printf "\n"
}

function cmd::logs::show_wg_events() {
  local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \
        limit="${4:-50}" collapse="${5:-1}" \
        since="${6:-}" filter_event="${7:-}" sort_order="${8:-desc}" \
        resolved_only="${9:-false}"
 
  [[ ! -f "$WG_EVENTS_LOG" ]] && return 0
 
  local data
  data=$(json::wg_events \
    "$WG_EVENTS_LOG" "$filter_name" "$filter_type" \
    "$limit" "$collapse" "$since" "$filter_event" \
    "$(ctx::endpoint_cache)" "$sort_order" \
    2>/dev/null)
 
  [[ -z "$data" ]] && return 0
 
  # ── Collect unique endpoints for batch resolution ──
  local -a ep_list=()
  while IFS='|' read -r ts client endpoint event count gap_seconds; do
    [[ -z "$ts" || -z "$endpoint" ]] && continue
    ep_list+=("$endpoint")
  done <<< "$data"
 
  declare -A resolve_cache=()
  if [[ ${#ep_list[@]} -gt 0 ]]; then
    while IFS='|' read -r ip name; do
      [[ -n "$ip" ]] && resolve_cache["$ip"]="$name"
    done < <(json::batch_resolve "${ep_list[@]}" 2>/dev/null)
  fi
 
  # ── Measure widths ──
  local w_client=16 w_endpoint=16
  local resolved_data=""
 
  while IFS='|' read -r ts client endpoint event count gap_seconds; do
    [[ -z "$ts" ]] && continue
    (( ${#client} > w_client )) && w_client=${#client}
 
    local resolved=""
    if [[ -n "$endpoint" ]]; then
      resolved="${resolve_cache[$endpoint]:-}"
      [[ "$resolved" == "$endpoint" ]] && resolved=""
    fi
 
    local ep_raw="${endpoint:--}"
    local ep_measure_len
    if $resolved_only; then
      local ep_display="${resolved:-$endpoint}"
      [[ -z "$ep_display" ]] && ep_display="-"
      ep_measure_len=${#ep_display}
    else
      ep_measure_len=${#ep_raw}
      [[ -n "$resolved" && -n "$endpoint" ]] && \
        ep_measure_len=$(( ${#endpoint} + 4 + ${#resolved} ))
    fi
    (( ep_measure_len > w_endpoint )) && w_endpoint=$ep_measure_len
 
    resolved_data+="${ts}|${client}|${endpoint}|${event}|${count}|${gap_seconds}|${resolved}"$'\n'
  done <<< "$data"
 
  (( w_client   += 2 ))
  (( w_endpoint += 2 ))
 
  # ── Render ──
  ui::logs::wg_section_header
  while IFS='|' read -r ts client endpoint event count gap_seconds resolved; do
    [[ -z "$ts" ]] && continue
    if $resolved_only; then
      local ep_display="${resolved:-$endpoint}"
      [[ -z "$ep_display" ]] && ep_display="-"
      ui::logs::wg_row "$ts" "$client" "$ep_display" "$event" \
        "$count" "$w_client" "$w_endpoint" "$gap_seconds" ""
    else
      ui::logs::wg_row "$ts" "$client" "$endpoint" "$event" \
        "$count" "$w_client" "$w_endpoint" "$gap_seconds" "$resolved"
    fi
  done <<< "$resolved_data"
  printf "\n"
}

function cmd::logs::show_merged() {
  local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \
        limit="${4:-50}" net_file="${5:-}" since="${6:-}"

  local fw_data wg_data
  fw_data=$(json::fw_events \
    "$FW_EVENTS_LOG" "$filter_ip" "$filter_type" \
    "$(ctx::clients)" "${net_file:-}" \
    "$limit" "1" "$since" "" "" \
    2>/dev/null)
  wg_data=$(json::wg_events \
    "$WG_EVENTS_LOG" "$filter_name" "$filter_type" \
    "$limit" "1" "$since" "" \
    2>/dev/null)

  local w_client=16 w_dest=20
  while IFS='|' read -r ts client rest; do
    [[ -z "$ts" ]] && continue
    (( ${#client} > w_client )) && w_client=${#client}
  done < <(echo "$fw_data"; echo "$wg_data")
  (( w_client += 2 ))

  local merged_data
  merged_data=$(
    while IFS='|' read -r ts client dest_ip dest_port proto svc count; do
      [[ -z "$ts" ]] && continue
      echo "fw|${ts}|${client}|${dest_ip}|${dest_port}|${proto}|${svc}|${count}"
    done <<< "$fw_data"
    while IFS='|' read -r ts client endpoint event count; do
      [[ -z "$ts" ]] && continue
      echo "wg|${ts}|${client}|${endpoint}|${event}|${count}"
    done <<< "$wg_data"
  )

  while IFS='|' read -r source ts rest; do
    [[ -z "$source" ]] && continue
    case "$source" in
      fw)
        IFS='|' read -r client dest_ip dest_port proto svc count <<< "$rest"
        local dest_display
        if [[ -n "$svc" ]]; then
          [[ -n "$dest_port" ]] && dest_display="${svc}/${proto}" || dest_display="${svc} (${proto})"
        else
          [[ -n "$dest_port" ]] && dest_display="${dest_ip}:${dest_port}/${proto}" || dest_display="${dest_ip} (${proto})"
        fi
        (( ${#dest_display} > w_dest )) && w_dest=${#dest_display}
        ;;
    esac
  done <<< "$merged_data"
  (( w_dest += 2 ))

  while IFS='|' read -r source ts rest; do
    [[ -z "$source" ]] && continue
    case "$source" in
      fw)
        IFS='|' read -r client dest_ip dest_port proto svc count <<< "$rest"
        ui::watch::fw_row "$ts" "$client" \
          "$(ui::logs::build_dest "$dest_ip" "$dest_port" "$proto" "$svc")" \
          "$w_client" "$w_dest"
        ;;
      wg)
        IFS='|' read -r client endpoint event count <<< "$rest"
        ui::watch::wg_row "$ts" "$client" "$endpoint" "$event" \
          "$w_client" "$w_dest"
        ;;
    esac
  done < <(echo "$merged_data" | sort -t'|' -k2,2)

  printf "\n"
}

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

  log::section "WireGuard Live Log (Ctrl+C to stop)"
  printf "\n"

  local restricted_only=false blocked_only=false
  $fw_only && restricted_only=true
  $wg_only && blocked_only=true

  monitor::live "$filter_name" "$filter_type" "" \
    "$blocked_only" "$restricted_only" "false" "false"
}

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

  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

  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]*) ;;
      *) 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::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})"
}