#!/usr/bin/env bash
# subnet.command.sh — manage the subnet map (subnets.json)
#
# Subcommands:
#   wgctl subnet list
#   wgctl subnet show  --name <name>
#   wgctl subnet add   --name <name> --subnet <cidr> [--type <type>]
#                      [--tunnel-mode split|full] [--desc <desc>]
#                      [--group <parent>]
#   wgctl subnet rm    --name <name>
#   wgctl subnet rename --name <old> --new-name <new>

# ============================================
# Lifecycle
# ============================================

function cmd::subnet::on_load() {
  flag::register --name
  flag::register --subnet
  flag::register --type
  flag::register --tunnel-mode
  flag::register --desc
  flag::register --group
  flag::register --new-name

  command::mixin json_output
}

# ============================================
# Help
# ============================================

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

Manage the subnet map.

Subcommands:
  list                          List all configured subnets
  show   --name <name>          Show details for a subnet
  add    --name <name>          Add a new subnet entry
         --subnet <cidr>
         [--type <type>]
         [--tunnel-mode split|full]
         [--desc <desc>]
         [--group <parent>]
  rm     --name <name>          Remove a subnet (refused if in use)
  rename --name <old>           Rename a subnet (refused if in use)
         --new-name <new>

Examples:
  wgctl subnet list
  wgctl subnet show --name guests
  wgctl subnet add --name iot-cctv --subnet 10.1.211.0/24 --type iot
  wgctl subnet add --name desktop --subnet 10.1.101.0/24 --group guests
  wgctl subnet rm --name iot-cctv
  wgctl subnet rename --name iot-cctv --new-name cctv
EOF
}

# ============================================
# Run
# ============================================

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

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

  case "$subcmd" in
    list)   cmd::subnet::_list   "$@" ;;
    show)   cmd::subnet::_show   "$@" ;;
    add)    cmd::subnet::_add    "$@" ;;
    rm)     cmd::subnet::_rm     "$@" ;;
    rename) cmd::subnet::_rename "$@" ;;
    --help) cmd::subnet::help        ;;
    *)
      log::error "Unknown subcommand '${subcmd}'. Available: list, show, add, rm, rename"
      return 1
      ;;
  esac
}

# ============================================
# Subcommands
# ============================================

function cmd::subnet::_list() {
  local data
  data=$(subnet::list_data | ui::sort_rows 1)
 
  if [[ -z "$data" ]]; then
    log::info "No subnets defined."
    return 0
  fi
 
  if display::is_table "subnet_list"; then
    cmd::subnet::_render_table "$data"
    return 0
  fi

  echo ""
  local prev_group=""
  while IFS='|' read -r display_name subnet type_key tunnel_mode desc is_group group_parent; do
    if [[ "$is_group" == "true" ]]; then
      # Print group parent header when we encounter first child
      if [[ "$group_parent" != "$prev_group" ]]; then
        [[ -n "$prev_group" ]] && ui::subnet::group_separator
        ui::subnet::row_group_parent "$group_parent"
        prev_group="$group_parent"
      fi
      ui::subnet::row_group_child "$type_key" "$subnet" "$tunnel_mode"
    else
      # Scalar entry
      [[ -n "$prev_group" ]] && ui::subnet::group_separator
      prev_group=""
      ui::subnet::row_scalar "$display_name" "$subnet" "$tunnel_mode"
    fi
  done <<< "$data"
  echo ""
}

function cmd::subnet::_render_table() {
  local data="${1:-}"
  [[ -z "$data" ]] && return 0
 
  ui::subnet::list_header_table
  while IFS='|' read -r type cidr display_name tunnel desc is_group group_parent; do
    [[ -z "$type" ]] && continue
    ui::subnet::list_row_table "$type" "$cidr" "$tunnel" "$desc"
  done <<< "$data"
  printf "\n"
}

function cmd::subnet::_maybe_group_separator() {
  local is_group="${1:-}" group_parent="${2:-}" prev_group="${3:-}"
  if [[ "$is_group" == "true" && "$group_parent" != "$prev_group" && -n "$prev_group" ]]; then
    ui::subnet::group_separator
  elif [[ "$is_group" == "false" && -n "$prev_group" ]]; then
    ui::subnet::group_separator
  fi
}

function cmd::subnet::_update_prev_group() {
  local is_group="${1:-}" group_parent="${2:-}" prev_group="${3:-}"
  if [[ "$is_group" == "true" ]]; then
    echo "$group_parent"
  else
    echo ""
  fi
}

function cmd::subnet::_show() {
  local name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name) name="$2"; shift 2 ;;
      --help) cmd::subnet::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done
 
  [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
  subnet::require_exists "$name" || return 1
 
  local data
  data=$(subnet::show_data "$name")
 
  local is_group="false"
  local show_name="" show_subnet="" show_tunnel="" show_desc=""
 
  while IFS='|' read -r key val rest; do
    case "$key" in
      name)        show_name="$val" ;;
      is_group)    is_group="$val" ;;
      subnet)      show_subnet="$val" ;;
      tunnel_mode) show_tunnel="$val" ;;
      desc)        show_desc="$val" ;;
    esac
  done <<< "$data"
 
  if [[ "$is_group" == "true" ]]; then
    # Group display
    ui::subnet::show_group "$show_name"
 
    while IFS='|' read -r key val rest; do
      [[ "$key" != "child" ]] && continue
      local c_type="$val"
      local c_subnet c_tunnel c_desc
      c_subnet=$(echo "$rest" | cut -d'|' -f1)
      c_tunnel=$(echo "$rest" | cut -d'|' -f2)
      c_desc=$(echo   "$rest" | cut -d'|' -f3)
      ui::subnet::show_child_row "$c_type" "$c_subnet" "$c_tunnel" "$c_desc"
    done <<< "$data"
 
    local peers_using
    peers_using=$(subnet::peers_using "$name")
    ui::subnet::show_peers_annotated "$peers_using" "$(ctx::subnets)"
  else
    # Scalar display
    ui::subnet::show_scalar "$show_name" "$show_subnet" "$show_tunnel" "$show_desc"
 
    local peers_using
    peers_using=$(subnet::peers_using "$name")
    ui::subnet::show_peers "$peers_using"
  fi
 
  echo ""
}

function cmd::subnet::_add() {
  local name="" cidr="" type_key="" tunnel_mode="split" desc="" group_parent=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)        name="$2";         shift 2 ;;
      --subnet)      cidr="$2";         shift 2 ;;
      --type)        type_key="$2";     shift 2 ;;
      --tunnel-mode) tunnel_mode="$2";  shift 2 ;;
      --desc)        desc="$2";         shift 2 ;;
      --group)       group_parent="$2"; shift 2 ;;
      --help)        cmd::subnet::help; return  ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" ]] && { log::error "Missing required flag: --name";   return 1; }
  [[ -z "$cidr" ]] && { log::error "Missing required flag: --subnet"; return 1; }

  cmd::subnet::_validate_tunnel_mode "$tunnel_mode" || return 1
  cmd::subnet::_validate_cidr        "$cidr"        || return 1

  json::subnet_add "$(ctx::subnets)" "$name" "$cidr" \
    "${type_key:-$name}" "$tunnel_mode" "$desc" "$group_parent"

  log::ok "Subnet '${name}' added (${cidr})"
}

function cmd::subnet::_rm() {
  local name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name) name="$2"; shift 2 ;;
      --help) cmd::subnet::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
  subnet::require_exists "$name" || return 1

  local peers_using
  peers_using=$(subnet::peers_using "$name")

  if [[ -n "$peers_using" ]]; then
    log::error "Cannot remove subnet '${name}' — in use by: ${peers_using//,/, }"
    log::error "Migrate or remove those peers first."
    return 1
  fi

  json::subnet_remove "$(ctx::subnets)" "$name" ""
  log::ok "Subnet '${name}' removed"
}

function cmd::subnet::_rename() {
  local name="" new_name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --name)     name="$2";     shift 2 ;;
      --new-name) new_name="$2"; shift 2 ;;
      --help)     cmd::subnet::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  [[ -z "$name" ]]     && { log::error "Missing required flag: --name";     return 1; }
  [[ -z "$new_name" ]] && { log::error "Missing required flag: --new-name"; return 1; }
  subnet::require_exists "$name" || return 1

  local peers_using
  peers_using=$(subnet::peers_using "$name")
  if [[ -n "$peers_using" ]]; then
    log::error "Cannot rename subnet '${name}' — configs already distributed to: ${peers_using//,/, }"
    log::error "Client configs reference the subnet CIDR which cannot change after distribution."
    return 1
  fi

  json::subnet_rename "$(ctx::subnets)" "$name" "$new_name" ""
  log::ok "Subnet '${name}' renamed to '${new_name}'"
}

# ============================================
# Validation Helpers
# ============================================

function cmd::subnet::_validate_tunnel_mode() {
  local mode="${1:-}"
  case "$mode" in
    split|full) return 0 ;;
    *)
      log::error "Invalid --tunnel-mode '${mode}'. Use: split, full"
      return 1
      ;;
  esac
}

function cmd::subnet::_validate_cidr() {
  local cidr="${1:-}"
  if ! echo "$cidr" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'; then
    log::error "Invalid CIDR format: '${cidr}'"
    return 1
  fi
}

function cmd::subnet::_output_json() {
  local data
  data=$(subnet::list_data 2>/dev/null)
 
  local -a subnets=()
  while IFS='|' read -r type cidr display_name tunnel_mode desc is_group group_parent; do
    [[ -z "$type" ]] && continue
 
    local is_group_json="false"
    [[ "$is_group" == "true" ]] && is_group_json="true"
 
    subnets+=("$(printf '{"type":"%s","cidr":"%s","display_name":"%s","tunnel_mode":"%s","desc":"%s","is_group":%s,"group_parent":"%s"}' \
      "$type" "$cidr" "$display_name" "$tunnel_mode" \
      "$desc" "$is_group_json" "${group_parent:-}")")
  done <<< "$data"
 
  local count=${#subnets[@]}
  local array
  array=$(printf '%s\n' "${subnets[@]:-}" | paste -sd ',' -)
  printf '{"subnets":[%s]}' "${array:-}" | json::envelope "subnet list" "$count"
}