#!/usr/bin/env bash

function cmd::net::on_load() {
  flag::register --name
  flag::register --ip
  flag::register --port
  flag::register --desc
  flag::register --tag
  flag::register --detailed
  flag::register --force

  command::mixin json_output
}

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

Manage named network services for use with block/allow rules.
Services map names to IPs and ports, making rules more readable.

Subcommands:
  list                        List all services
  show --name <name>          Show service details
  add --name <name> --ip <ip> Add a service
  add --name <svc:port-name> --port <port:proto>
                              Add a port to a service
  rm --name <name>            Remove service or port
  rm --name <svc:ports>       Remove all ports from service

Options for add (service):
  --name <name>               Service name (e.g. proxmox)
  --ip <ip>                   Service IP address
  --desc <description>        Optional description
  --tag <tag>                 Optional tag (repeatable)

Options for add (port):
  --name <svc:port-name>      Service:port-name (e.g. proxmox:web-ui)
  --port <port:proto>         Port and protocol (e.g. 8006:tcp)
  --desc <description>        Optional description

Options for list:
  --detailed                  Show ports for each service
  --tag <tag>                 Filter by tag

Examples:
  wgctl net list
  wgctl net list --detailed
  wgctl net list --tag admin
  wgctl net show --name proxmox
  wgctl net add --name proxmox --ip 10.0.0.100 --desc "Proxmox VE"
  wgctl net add --name proxmox:web-ui --port 8006:tcp --desc "Web UI"
  wgctl net add --name proxmox:ssh --port 22:tcp
  wgctl net rm --name proxmox:web-ui
  wgctl net rm --name proxmox:ports
  wgctl net rm --name proxmox
EOF
}

function cmd::net::run() {
  local subcmd="${1:-list}"
  shift || true

  if command::json; then
    cmd::net::_output_json
    return 0
  fi

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

# ============================================
# List
# ============================================

function cmd::net::list() {
  local detailed=false filter_tag=""
 
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --detailed) detailed=true;    shift   ;;
      --tag)      filter_tag="$2";  shift 2 ;;
      --help)     cmd::net::help;   return  ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done
 
  local net_file
  net_file="$(ctx::net)"
 
  if [[ ! -f "$net_file" ]]; then
    log::wg_warning "No services configured. Use 'wgctl net add' to add one."
    return 0
  fi
 
  # Collect filtered data and build ports display per service
  local filtered_data=""
  while IFS="|" read -r name ip desc tags port_count; do
    [[ -z "$name" ]] && continue
    [[ -n "$filter_tag" && "$tags" != *"$filter_tag"* ]] && continue
 
    # Build ports display from json::net_show
    local ports_display=""
    while IFS="|" read -r ptype pname pport pproto pdesc; do
      [[ "$ptype" != "port" ]] && continue
      local port_str=":${pport}"
      [[ -n "$pproto" && "$pproto" != "tcp" ]] && port_str="${port_str}/${pproto}"
      ports_display+="${port_str}, "
    done < <(json::net_show "$net_file" "$name")
    ports_display="${ports_display%, }"
    [[ -z "$ports_display" ]] && ports_display="-"
 
    filtered_data+="${name}|${ip}|${desc}|${tags}|${ports_display}"$'\n'
  done < <(json::net_list "$net_file")
 
  [[ -z "$filtered_data" ]] && {
    [[ -n "$filter_tag" ]] && \
      log::wg_warning "No services with tag: ${filter_tag}" || \
      log::wg_warning "No services configured"
    return 0
  }

  # Measure column widths
  local w_name=12 w_ip=13 w_ports=16
  while IFS="|" read -r name ip desc tags ports; do
    [[ -z "$name" ]] && continue
    (( ${#name}  > w_name  )) && w_name=${#name}
    (( ${#ip}    > w_ip    )) && w_ip=${#ip}
    (( ${#ports} > w_ports )) && w_ports=${#ports}
  done <<< "$filtered_data"
  (( w_name  += 2 ))
  (( w_ip    += 2 ))
  (( w_ports += 2 ))
 
  log::section "Network Services"
  echo ""

  if display::is_table "net_list"; then
    cmd::net::_render_table "$filtered_data"
    return 0
  fi
 
  while IFS="|" read -r name ip desc tags ports; do
    [[ -z "$name" ]] && continue
    ui::net::list_row "$name" "$ip" "$desc" "$tags" "$ports" \
      "$w_name" "$w_ip" "$w_ports"
 
    if $detailed; then
      while IFS="|" read -r ptype pname pport pproto pdesc; do
        [[ "$ptype" != "port" ]] && continue
        ui::net::show_port_row "$pname" "$pport" "$pproto" "$pdesc"
      done < <(json::net_show "$net_file" "$name")
      echo ""
    fi
  done <<< "$filtered_data"
 
  echo ""
}

function cmd::net::_render_table() {
  local data="${1:-}"
  [[ -z "$data" ]] && return 0
 
  ui::net::list_header_table
  while IFS='|' read -r name ip desc tags port_count; do
    [[ -z "$name" ]] && continue
    ui::net::list_row_table "$name" "$ip" "$desc" "$tags" "$port_count"
  done <<< "$data"
}

# ============================================
# Show
# ============================================

function cmd::net::show() {
  local name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name) util::require_flag "--name" "${2:-}" || return 1
              name="$2"; shift 2 ;;
      --help) cmd::net::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done
 
  [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
  net::require_exists "$name" || return 1
 
  log::section "Service: ${name}"
  printf "\n"
 
  local has_ports=false
  while IFS="|" read -r key val1 val2 val3 val4; do
    case "$key" in
      name) ui::row "Name"        "$val1" ;;
      desc) ui::row "Description" "${val1:-—}" ;;
      tags) ui::row "Tags"        "${val1:-—}" ;;
      ip)   ui::row "IP"          "$val1" ;;
      port)
        if ! $has_ports; then
          printf "  %-20s\n" "Ports:"
          has_ports=true
        fi
        ui::net::show_port_row "$val1" "$val2" "$val3" "$val4"
        ;;
    esac
  done < <(json::net_show "$(ctx::net)" "$name")
 
  printf "\n"
}

# ============================================
# Add
# ============================================

function cmd::net::add() {
  local name="" ip="" port="" desc="" tags=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name) util::require_flag "--name" "${2:-}" || return 1
              name="$2"; shift 2 ;;
      --ip)   ip="$2";   shift 2 ;;
      --port) port="$2"; shift 2 ;;
      --desc) desc="$2"; shift 2 ;;
      --tag)  tags+=("$2"); shift 2 ;;
      --help) cmd::net::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1

  if [[ "$name" == *:* ]]; then
    # Port mode: proxmox:web-ui
    local svc_name="${name%%:*}"
    local port_name="${name##*:}"

    [[ -z "$port" ]] && log::error "Missing required flag: --port" && return 1
    net::require_exists "$svc_name" || return 1

    local port_num proto
    if [[ "$port" == *:* ]]; then
      port_num="${port%%:*}"
      proto="${port##*:}"
    else
      port_num="$port"
      proto="tcp"
    fi

    json::net_add_port "$(ctx::net)" "$svc_name" "$port_name" \
      "$port_num" "$proto" "$desc"

    log::wg_success "Added port: ${svc_name}:${port_name} → ${port_num}/${proto}"
  else
    # Service mode: proxmox
    [[ -z "$ip" ]] && log::error "Missing required flag: --ip" && return 1

    local tags_str
    tags_str=$(IFS=','; echo "${tags[*]}")

    json::net_add_service "$(ctx::net)" "$name" "$ip" "$desc" "$tags_str"

    log::wg_success "Service added: ${name} → ${ip}"
  fi
  return 0
}

# ============================================
# Remove
# ============================================

function cmd::net::rm() {
  local name="" force=false

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

  [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1

  # Validate existence
  if [[ "$name" == *:* ]]; then
    local svc_name="${name%%:*}"
    local port_name="${name##*:}"
    if [[ "$port_name" != "ports" ]]; then
      # Check specific port exists
      local exists
      exists=$(json::net_exists "$(ctx::net)" "$name")
      if [[ "$exists" != "true" ]]; then
        log::error "Port not found: ${name}"
        return 1
      fi
    else
      net::require_exists "$svc_name" || return 1
    fi
  else
    net::require_exists "$name" || return 1
  fi

  if ! $force; then
    local what="service '${name}'"
    [[ "$name" == *:ports ]] && what="all ports from '${name%%:*}'"
    [[ "$name" == *:* && "$name" != *:ports ]] && what="port '${name}'"
    read -r -p "Remove ${what}? [y/N] " confirm
    case "$confirm" in
      [yY]*) ;;
      *) log::info "Aborted"; return 0 ;;
    esac
  fi

  json::net_remove "$(ctx::net)" "$name"
  log::wg_success "Removed: ${name}"
  return 0
}

function cmd::net::_output_json() {
  local net_file
  net_file="$(ctx::net)"
  local data
  data=$(json::net_list "$net_file" 2>/dev/null)
 
  local -a services=()
  while IFS='|' read -r name ip desc tags port_count; do
    [[ -z "$name" ]] && continue
    # Build tags array
    local tags_json="[]"
    if [[ -n "$tags" ]]; then
      local tags_array
      tags_array=$(echo "$tags" | tr ',' '\n' | \
        while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//')
      tags_json="[${tags_array}]"
    fi
    services+=("$(printf '{"name":"%s","ip":"%s","desc":"%s","tags":%s,"port_count":%s}' \
      "$name" "$ip" "$desc" "$tags_json" "$port_count")")
  done <<< "$data"
 
  local count=${#services[@]}
  local array
  array=$(printf '%s\n' "${services[@]:-}" | paste -sd ',' -)
  printf '{"services":[%s]}' "${array:-}" | json::envelope "net list" "$count"
}