#!/usr/bin/env bash
# commands/export.command.sh

function cmd::export::on_load() {
  flag::register --peer
  flag::register --identity
  flag::register --all
  flag::register --out
  flag::register --conf-only
  flag::register --meta-only
  flag::register --no-config
  flag::register --no-peers
  flag::register --force
}

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

Export wgctl data as a portable JSON bundle.

Options:
  --peer <name>       Export a single peer (conf, meta, groups, identity, blocks)
  --identity <name>   Export an identity
  --all               Full backup (all peers, rules, identities, groups, etc.)
  --out <file>        Write to file instead of stdout
  --conf-only         Export peer conf only (with --peer)
  --meta-only         Export peer meta only (with --peer)
  --no-config         Skip wgctl.json (with --all)
  --no-peers          Skip peer confs (with --all)
  --force             Overwrite existing output file

Examples:
  wgctl export --peer phone-nuno
  wgctl export --peer phone-nuno --out phone-nuno.json
  wgctl export --identity nuno --out nuno.json
  wgctl export --all --out backup.json
  wgctl export --all --no-config --out data-only.json
EOF
}

function cmd::export::run() {
  local peer="" identity="" all=false out=""
  local conf_only=false meta_only=false
  local no_config=false no_peers=false force=false

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --peer)       peer="$2";        shift 2 ;;
      --identity)   identity="$2";    shift 2 ;;
      --all)        all=true;         shift   ;;
      --out)        out="$2";         shift 2 ;;
      --conf-only)  conf_only=true;   shift   ;;
      --meta-only)  meta_only=true;   shift   ;;
      --no-config)  no_config=true;   shift   ;;
      --no-peers)   no_peers=true;    shift   ;;
      --force)      force=true;       shift   ;;
      --help)       cmd::export::help; return ;;
      *) log::error "Unknown flag: $1"; return 1 ;;
    esac
  done

  # Validate
  local mode_count=0
  [[ -n "$peer" ]]    && (( mode_count++ )) || true
  [[ -n "$identity" ]] && (( mode_count++ )) || true
  $all                && (( mode_count++ )) || true

  if [[ "$mode_count" -eq 0 ]]; then
    log::error "Specify --peer, --identity, or --all"
    cmd::export::help
    return 1
  fi
  if [[ "$mode_count" -gt 1 ]]; then
    log::error "Only one of --peer, --identity, --all can be used at a time"
    return 1
  fi

  # Check output file
  if [[ -n "$out" && -f "$out" && ! $force ]]; then
    log::error "Output file already exists: ${out} (use --force to overwrite)"
    return 1
  fi

  local json=""
  if [[ -n "$peer" ]]; then
    json=$(cmd::export::_peer "$peer" "$conf_only" "$meta_only") || return 1
  elif [[ -n "$identity" ]]; then
    json=$(cmd::export::_identity "$identity") || return 1
  elif $all; then
    json=$(cmd::export::_full "$no_config" "$no_peers") || return 1
  fi

  if [[ -n "$out" ]]; then
    echo "$json" > "$out"
    log::wg_success "Exported to ${out}"
  else
    echo "$json"
  fi
}

# ======================================================
# Peer export
# ======================================================

function cmd::export::_peer() {
  local name="${1:-}" conf_only="${2:-false}" meta_only="${3:-false}"

  peers::require_exists "$name" || return 1

  local conf_file
  conf_file="$(ctx::clients)/${name}.conf"
  [[ ! -f "$conf_file" ]] && log::error "Client conf not found: ${conf_file}" && return 1

  local conf_b64
  conf_b64=$(base64 -w 0 < "$conf_file" 2>/dev/null || base64 < "$conf_file")

  if $conf_only; then
    cmd::export::_envelope "peer_conf" \
      "$(printf '{"name":"%s","conf":"%s"}' "$name" "$conf_b64")"
    return 0
  fi

  # Meta
  local meta_file meta_json="{}"
  meta_file="$(ctx::meta)/${name}.meta"
  [[ -f "$meta_file" ]] && meta_json=$(cat "$meta_file")

  if $meta_only; then
    cmd::export::_envelope "peer_meta" \
      "$(printf '{"name":"%s","meta":%s}' "$name" "$meta_json")"
    return 0
  fi

  # Public key
  local public_key=""
  local key_file
  key_file="$(ctx::clients)/${name}_public.key"
  [[ -f "$key_file" ]] && public_key=$(cat "$key_file")

  # IP
  local ip
  ip=$(peers::get_ip "$name")

  # Type
  local peer_type
  peer_type=$(peers::get_type "$name" 2>/dev/null || echo "")

  # Direct rule
  local direct_rule
  direct_rule=$(peers::get_meta "$name" "rule" 2>/dev/null || echo "")

  # Identity
  local identity
  identity=$(peers::get_identity "$name" 2>/dev/null || echo "")

  # Groups
  local -a group_list=()
  while IFS= read -r g; do
    [[ -n "$g" ]] && group_list+=("\"$g\"")
  done < <(json::peer_groups "$(ctx::groups)" "$name" 2>/dev/null)
  local groups_json="[]"
  [[ ${#group_list[@]} -gt 0 ]] && \
    groups_json="[$(printf '%s,' "${group_list[@]}" | sed 's/,$//')]"

  # Blocks
  local block_file is_blocked="false" block_json="null"
  block_file="$(ctx::blocks)/${name}.block"
  if [[ -f "$block_file" ]]; then
    is_blocked="true"
    block_json=$(base64 -w 0 < "$block_file" 2>/dev/null || base64 < "$block_file")
    block_json="\"${block_json}\""
  fi

  local peer_data
  peer_data=$(printf \
    '{"name":"%s","ip":"%s","type":"%s","public_key":"%s","conf":"%s","meta":%s,"identity":"%s","groups":%s,"direct_rule":"%s","blocks":{"is_blocked":%s,"block_file":%s}}' \
    "$name" "$ip" "$peer_type" "$public_key" "$conf_b64" \
    "$meta_json" "$identity" "$groups_json" "$direct_rule" \
    "$is_blocked" "$block_json")

  cmd::export::_envelope "peer" "$peer_data"
}

# ======================================================
# Identity export
# ======================================================

function cmd::export::_identity() {
  local name="${1:-}"
  identity::require_exists "$name" || return 1

  local id_file
  id_file="$(ctx::identities)/${name}.identity"
  local id_json
  id_json=$(cat "$id_file")

  cmd::export::_envelope "identity" \
    "$(printf '{"name":"%s","identity":%s}' "$name" "$id_json")"
}

# ======================================================
# Full backup
# ======================================================

function cmd::export::_full() {
  local no_config="${1:-false}" no_peers="${2:-false}"
  local version
  version=$(wgctl::version 2>/dev/null || echo "unknown")
 
  python3 "$(ctx::json_helper)" export_full \
    "$(ctx::clients)" \
    "$(ctx::meta)" \
    "$(ctx::rules)" \
    "$(ctx::identities)" \
    "$(ctx::groups)" \
    "$(ctx::blocks)" \
    "$(ctx::block_history)" \
    "$(ctx::config_file)" \
    "$(ctx::policies)" \
    "$(ctx::subnets)" \
    "$(ctx::net)" \
    "$(ctx::hosts)" \
    "$no_config" \
    "$no_peers" \
    "$version" \
    2>/dev/null
}

# Helper — peer data without envelope (used by full backup)
function cmd::export::_peer_data() {
  local name="${1:-}"
  local conf_file
  conf_file="$(ctx::clients)/${name}.conf"
  [[ ! -f "$conf_file" ]] && return 0

  local conf_b64
  conf_b64=$(base64 -w 0 < "$conf_file" 2>/dev/null || base64 < "$conf_file")

  local meta_file meta_json="{}"
  meta_file="$(ctx::meta)/${name}.meta"
  [[ -f "$meta_file" ]] && meta_json=$(cat "$meta_file")

  local public_key=""
  local key_file
  key_file="$(ctx::clients)/${name}_public.key"
  [[ -f "$key_file" ]] && public_key=$(cat "$key_file")

  local ip
  ip=$(peers::get_ip "$name")

  local peer_type
  peer_type=$(peers::get_type "$name" 2>/dev/null || echo "")

  local direct_rule
  direct_rule=$(peers::get_meta "$name" "rule" 2>/dev/null || echo "")

  local identity
  identity=$(peers::get_identity "$name" 2>/dev/null || echo "")

  local -a group_list=()
  while IFS= read -r g; do
    [[ -n "$g" ]] && group_list+=("\"$g\"")
  done < <(json::peer_groups "$(ctx::groups)" "$name" 2>/dev/null)
  local groups_json="[]"
  [[ ${#group_list[@]} -gt 0 ]] && \
    groups_json="[$(printf '%s,' "${group_list[@]}" | sed 's/,$//')]"

  local block_file is_blocked="false" block_json="null"
  block_file="$(ctx::blocks)/${name}.block"
  if [[ -f "$block_file" ]]; then
    is_blocked="true"
    block_json="\"$(base64 -w 0 < "$block_file" 2>/dev/null || base64 < "$block_file")\""
  fi

  printf \
    '{"name":"%s","ip":"%s","type":"%s","public_key":"%s","conf":"%s","meta":%s,"identity":"%s","groups":%s,"direct_rule":"%s","blocks":{"is_blocked":%s,"block_file":%s}}' \
    "$name" "$ip" "$peer_type" "$public_key" "$conf_b64" \
    "$meta_json" "$identity" "$groups_json" "$direct_rule" \
    "$is_blocked" "$block_json"
}

# ======================================================
# Envelope helper
# ======================================================

function cmd::export::_envelope() {
  local export_type="${1:-}" data="${2:-}"
  local version ts
  version=$(wgctl::version 2>/dev/null || echo "unknown")
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
  printf '{"wgctl_version":"%s","export_type":"%s","exported_at":"%s","data":%s}\n' \
    "$version" "$export_type" "$ts" "$data"
}

function cmd::export::_compact_json() {
  local file="$1"
  python3 -c "
import json, sys
try:
    print(json.dumps(json.load(open('${file}'))))
except Exception as e:
    print('{}', file=sys.stderr)
" 2>/dev/null
}