#!/usr/bin/env bash
# commands/activity/helpers.sh
# Implementation logic — render functions, data fetching, maps

# cmd::activity::_impl <filter_peer> <filter_service> <filter_ip> <filter_type>
#                      <hours> <accept_only> <drop_only> <external_only>
#                      <show_ports> <exclude_str>
function cmd::activity::_impl() {
  local filter_peer="$1" filter_service="$2" filter_ip="$3" filter_type="$4"
  local hours="$5"
  local accept_only="$6" drop_only="$7" external_only="$8"
  local show_ports="$9" exclude_str="${10}"

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

  local service_ip=""
  if [[ -n "$filter_service" ]]; then
    service_ip=$(net::resolve "$filter_service" 2>/dev/null | head -1 | cut -d: -f1) || true
    [[ -z "$service_ip" ]] && log::error "Service not found: ${filter_service}" && return 1
  fi
  [[ -n "$filter_ip" ]] && service_ip="$filter_ip"

  # ── Fetch data ──
  local data=""
  if [[ "$accept_only" != "true" ]]; then
    data=$(json::activity_aggregate \
      "$(ctx::fw_events_log)" "$(ctx::events_log)" \
      "$(config::interface)" "$(ctx::net)" \
      "$(ctx::clients)" "$(ctx::meta)" \
      "$hours" "$filter_peer" "$service_ip" "$exclude_str" 2>/dev/null)
  fi

  local accept_data=""
  if [[ "$drop_only" != "true" ]]; then
    local since_arg="" ext_flag="0"
    [[ "$hours" -gt 0 ]] && since_arg="${hours}h"
    [[ "$external_only" == "true" ]] && ext_flag="1"
    [[ -f "$(ctx::accept_events_log)" ]] && \
      accept_data=$(json::accept_aggregate \
        "$(ctx::accept_events_log)" "$(ctx::net)" "$(ctx::clients)" \
        "$since_arg" "$filter_peer" "$ext_flag" "$exclude_str" 2>/dev/null)
  fi

  [[ -z "$data" && -z "$accept_data" ]] && \
    log::wg_warning "No activity data found" && return 0

  # ── Build accept maps ──
  declare -gA _ACCEPT_PEER=()
  declare -gA _ACCEPT_DEST_KEYS=()
  declare -gA _ACCEPT_DEST=()

  while IFS='|' read -r type rest; do
    [[ -z "$type" ]] && continue
    case "$type" in
      peer)
        local a_name a_bi a_bo a_pi a_po a_conns
        IFS='|' read -r a_name a_bi a_bo a_pi a_po a_conns <<< "$rest"
        _ACCEPT_PEER["$a_name"]="${a_bi}|${a_bo}|${a_pi}|${a_po}|${a_conns}"
        ;;
      dest)
        local d_peer d_ip d_port d_proto d_bytes_orig d_bytes_reply d_count
        IFS='|' read -r d_peer d_ip d_port d_proto d_bytes_orig d_bytes_reply d_count <<< "$rest"
        local d_key="${d_peer}:${d_ip}:${d_port}:${d_proto}"
        _ACCEPT_DEST["$d_key"]="${d_bytes_orig}|${d_bytes_reply}|${d_count}"
        _ACCEPT_DEST_KEYS["$d_peer"]+="${d_key} "
        ;;
    esac
  done <<< "$accept_data"

  # ── Measure column widths ──
  local w_peer=16 w_count=1

  while IFS='|' read -r type rest; do
    case "$type" in
      peer)
        local name drops
        name=$(echo "$rest"  | cut -d'|' -f1)
        drops=$(echo "$rest" | cut -d'|' -f4)
        (( ${#name}  > w_peer  )) && w_peer=${#name}
        (( ${#drops} > w_count )) && w_count=${#drops}
        ;;
      service)
        local svc_count
        svc_count=$(echo "$rest" | cut -d'|' -f3)
        (( ${#svc_count} > w_count )) && w_count=${#svc_count}
        ;;
    esac
  done <<< "$data"

  for a_name in "${!_ACCEPT_PEER[@]}"; do
    (( ${#a_name} > w_peer )) && w_peer=${#a_name}
    local a_conns_val="${_ACCEPT_PEER[$a_name]##*|}"
    (( ${#a_conns_val} > w_count )) && w_count=${#a_conns_val}
  done
  for key in "${!_ACCEPT_DEST[@]}"; do
    local d_val="${_ACCEPT_DEST[$key]}"
    local d_count_val="${d_val##*|}"
    (( ${#d_count_val} > w_count )) && w_count=${#d_count_val}
  done

  (( w_peer += 2 ))
  local drops_col=$(( w_peer + 30 ))

  local hours_display="${hours}h"
  [[ "$hours" == "0" ]] && hours_display="all time"

  log::section "Activity Monitor (last ${hours_display})"
  echo ""

  if display::is_table "activity"; then
    cmd::activity::_render_table "$data"
    return 0
  fi

  # ── Batch resolve accept dest IPs ──
  declare -gA _DEST_RESOLVE_CACHE=()
  local -a _dest_specs=()
  for _dk in "${!_ACCEPT_DEST[@]}"; do
    local _rest="${_dk#*:}"
    local _dip="${_rest%%:*}"
    local _pp="${_rest#*:}"
    local _dport="${_pp%%:*}"
    local _dproto="${_pp##*:}"
    local _spec="${_dip}:${_dport}:${_dproto}"
    local _found=false
    for _s in "${_dest_specs[@]:-}"; do
      [[ "$_s" == "$_spec" ]] && _found=true && break
    done
    $_found || _dest_specs+=("$_spec")
  done

  if [[ ${#_dest_specs[@]} -gt 0 ]]; then
    while IFS='|' read -r _spec _display; do
      [[ -n "$_spec" ]] && _DEST_RESOLVE_CACHE["$_spec"]="$_display"
    done < <(json::batch_resolve_dest "${_dest_specs[@]}" 2>/dev/null)
  fi

  # ── Accept dest inline renderer ──
  _render_peer_accept_dests() {
    local peer_name="$1"
    local keys="${_ACCEPT_DEST_KEYS[$peer_name]:-}"
    [[ -z "$keys" ]] && return 0
    for d_key in $keys; do
      local dest_stats="${_ACCEPT_DEST[$d_key]:-}"
      [[ -z "$dest_stats" ]] && continue
      local d_bytes_orig d_bytes_reply d_count
      IFS='|' read -r d_bytes_orig d_bytes_reply d_count <<< "$dest_stats"
      local rest_key="${d_key#${peer_name}:}"
      local d_ip="${rest_key%%:*}"
      local pp="${rest_key#*:}"
      local d_port="${pp%%:*}"
      local d_proto="${pp##*:}"
      local spec="${d_ip}:${d_port}:${d_proto}"
      local resolved="${_DEST_RESOLVE_CACHE[$spec]:-${d_ip}:${d_port}/${d_proto}}"
      local dest_display="$resolved"
      if [[ "$show_ports" == "true" && "$resolved" != "${d_ip}:"* && "$resolved" != "${d_ip} "* ]]; then
        if [[ -n "$d_port" && "$d_port" != "0" ]]; then
          dest_display=$(printf "%s \033[2m(%s:%s)\033[0m" "$resolved" "$d_ip" "$d_port")
        else
          dest_display=$(printf "%s \033[2m(%s)\033[0m" "$resolved" "$d_ip")
        fi
      fi
      ui::activity::accept_dest_row \
        "$dest_display" "$d_bytes_orig" "$d_bytes_reply" \
        "$d_count" "$drops_col" "$w_count"
    done
  }

  local first_peer=true skip_peer=false current_name=""
  local -a rendered_peers=()

  # ── Main render loop ──
  while IFS='|' read -r record_type rest; do
    case "$record_type" in
      peer)
        local name rx tx drops
        IFS='|' read -r name rx tx drops <<< "$rest"

        [[ -n "$current_name" ]] && [[ "$drop_only" != "true" ]] && \
          _render_peer_accept_dests "$current_name"

        skip_peer=false
        current_name="$name"
        local has_accept="${_ACCEPT_PEER[$name]:-}"

        $first_peer || echo ""
        first_peer=false
        rendered_peers+=("$name")

        local rx_fmt tx_fmt
        rx_fmt=$(fmt::bytes "$rx")
        tx_fmt=$(fmt::bytes "$tx")
        local name_pad rx_pad tx_pad
        name_pad=$(printf "%-${w_peer}s" "$name")
        rx_pad=$(printf   "%-10s"        "$rx_fmt")
        tx_pad=$(printf   "%-10s"        "$tx_fmt")

        local drop_word="drops"
        [[ "$drops" -eq 1 ]] && drop_word="drop"

        if [[ "$accept_only" == "true" ]]; then
          printf "  \033[1m%s\033[0m\n" "$name_pad"
        else
          ui::activity::peer_row \
            "$name_pad" "$rx_pad" "$tx_pad" "$drops" "$drop_word" "$w_count"
        fi

        if [[ -n "$has_accept" && "$drop_only" != "true" ]]; then
          local a_bi a_bo a_pi a_po a_conns
          IFS='|' read -r a_bi a_bo a_pi a_po a_conns <<< "$has_accept"
          ui::activity::accept_row \
            "$name_pad" \
            "$(printf '%-10s' "$(fmt::bytes "$a_bi")")" \
            "$(printf '%-10s' "$(fmt::bytes "$a_bo")")" \
            "$a_conns" "$w_count"
        fi
        ;;

      service)
        $skip_peer && continue
        [[ "$accept_only" == "true" ]] && continue
        local peer dest_display dst_ip dst_port proto drop_count
        IFS='|' read -r peer dest_display dst_ip dst_port proto drop_count <<< "$rest"
        local svc_display="$dest_display"
        if [[ "$show_ports" == "true" && -n "$dst_ip" ]]; then
          if [[ -n "$dst_port" ]]; then
            svc_display=$(printf "%s \033[2m(%s:%s)\033[0m" \
              "$dest_display" "$dst_ip" "$dst_port")
          else
            svc_display=$(printf "%s \033[2m(%s)\033[0m" \
              "$dest_display" "$dst_ip")
          fi
        fi
        local svc_drop_word="drops"
        [[ "$drop_count" -eq 1 ]] && svc_drop_word="drop"
        ui::activity::service_row \
          "$svc_display" "$drop_count" "$svc_drop_word" "$drops_col" "$w_count"
        ;;
    esac
  done <<< "$data"

  [[ -n "$current_name" ]] && [[ "$drop_only" != "true" ]] && \
    _render_peer_accept_dests "$current_name"

  # ── Accept-only peers ──
  if [[ "$drop_only" != "true" ]]; then
    for a_name in $(echo "${!_ACCEPT_PEER[@]}" | tr ' ' '\n' | sort); do
      local already=false
      for rp in "${rendered_peers[@]:-}"; do
        [[ "$rp" == "$a_name" ]] && already=true && break
      done
      $already && continue

      $first_peer || echo ""
      first_peer=false

      local a_stats="${_ACCEPT_PEER[$a_name]}"
      local a_bi a_bo a_pi a_po a_conns
      IFS='|' read -r a_bi a_bo a_pi a_po a_conns <<< "$a_stats"
      local name_pad
      name_pad=$(printf "%-${w_peer}s" "$a_name")
      printf "  \033[1m%s\033[0m\n" "$name_pad"
      ui::activity::accept_row \
        "$name_pad" \
        "$(printf '%-10s' "$(fmt::bytes "$a_bi")")" \
        "$(printf '%-10s' "$(fmt::bytes "$a_bo")")" \
        "$a_conns" "$w_count"
      _render_peer_accept_dests "$a_name"
    done
  fi

  echo ""
}

function cmd::activity::_render_table() {
  local data="${1:-}"
  [[ -z "$data" ]] && return 0
  ui::activity::header_table
  local skip_peer=false
  while IFS='|' read -r record_type rest; do
    case "$record_type" in
      peer)
        local name rx tx drops
        IFS='|' read -r name rx tx drops <<< "$rest"
        skip_peer=false
        local rx_fmt tx_fmt
        rx_fmt=$(fmt::bytes "$rx")
        tx_fmt=$(fmt::bytes "$tx")
        local drop_word="drops"
        [[ "$drops" -eq 1 ]] && drop_word="drop"
        ui::activity::peer_row_table "$name" "$rx_fmt" "$tx_fmt" "$drops" "$drop_word"
        ;;
      service)
        $skip_peer && continue
        local peer dest_display drop_count
        IFS='|' read -r peer dest_display drop_count <<< "$rest"
        ui::activity::service_row_table "$dest_display" "$drop_count" "drops"
        ;;
    esac
  done <<< "$data"
}

function cmd::activity::_output_json() {
  local hours="${1:-24}"
  local data
  data=$(json::activity_aggregate \
    "$(ctx::fw_events_log)" "$(ctx::events_log)" \
    "$(config::interface)" "$(ctx::net)" \
    "$(ctx::clients)" "$(ctx::meta)" \
    "$hours" "" "" "" 2>/dev/null)
  local -a peers=()
  while IFS='|' read -r record_type rest; do
    [[ "$record_type" != "peer" ]] && continue
    local name rx tx drops
    IFS='|' read -r name rx tx drops <<< "$rest"
    peers+=("{\"name\":\"${name}\",\"rx\":${rx},\"tx\":${tx},\"drops\":${drops}}")
  done <<< "$data"
  local array
  array=$(IFS=','; echo "${peers[*]:-}")
  printf '[%s]' "$array" | json::envelope "activity" "${#peers[@]}"
}