#!/usr/bin/env fish

### ------- helpers ---------------------------------------------------------

function _need --description "ensure a command exists"
  for c in $argv
    if not type -q $c
      echo (set_color red)"Error:"(set_color normal)" missing required command: $c" >&2
      return 1
    end
  end
end

function _banner --description "pretty section header" -a MSG
  set_color cyan
  echo
  echo "==> $MSG"
  set_color normal
end

# 32-char alphanumeric generator (fast, no shell-specials)
function _rand_alnum32
  if type -q openssl
    openssl rand -base64 48 2>/dev/null | tr -dc 'A-Za-z0-9' | head -c 32
  else
    dd if=/dev/urandom bs=48 count=1 2>/dev/null | base64 | tr -dc 'A-Za-z0-9' | head -c 32
  end
end

# Convert any string to a k8s-safe name (RFC-1123 label-ish)
function _k8s_name --description "sanitize to RFC-1123 label" -a S
  set s (string lower -- $S)
  set s (string replace -ar -- '[^a-z0-9-]' '-' $s)
  set s (string replace -r  -- '^-+' ''      $s)
  set s (string replace -r  -- '-+$' ''      $s)
  echo $s
end

# Simple non-empty check
function _require --description "require a non-empty value" -a WHAT VAL
  if test -z "$VAL"
    echo (set_color red)"Error:"(set_color normal)" $WHAT cannot be empty. Aborting." >&2
    return 1
  end
end

# List SCs in a stable way
function _sc_list --description "list storageclasses as menu"
  kubectl get storageclass -o json | jq -r '
    .items[]
    | {name: .metadata.name,
       provisioner: .provisioner,
       reclaim: (.reclaimPolicy // "Delete"),
       default: ( .metadata.annotations["storageclass.kubernetes.io/is-default-class"] == "true"
                  or .metadata.annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true") }
    | "\(.name)\t\(.provisioner)\t\(.reclaim)\t\((.default|tostring))"
  ' 2>/dev/null
end

# convert XMR -> piconero (atomic units) with Python (exact
function _xmr_to_atomic -a AMT
  _need python3; or return 1
  printf '%s' 'from decimal import Decimal, getcontext
import sys
getcontext().prec = 50
amt = Decimal(sys.argv[1])
atomic = int((amt * Decimal("1000000000000")).to_integral_value(rounding="ROUND_HALF_UP"))
print(atomic)
' | python3 - "$AMT"
end

# Choose SC (menu -> stderr; selection -> stdout)
function _choose_sc --description "interactive StorageClass picker"
  set lines (_sc_list)
  if test (count $lines) -eq 0
    echo (set_color red)"Error:"(set_color normal)" No StorageClasses found." >&2
    return 1
  end

  echo "" >&2
  echo "==> Available StorageClasses" >&2
  echo "  # | name                 | provisioner            | reclaim | default" >&2
  echo "----+----------------------+------------------------+---------+--------" >&2
  set i 1
  for l in $lines
    set name (echo $l | awk -F'\t' '{print $1}')
    set prov (echo $l | awk -F'\t' '{print $2}')
    set rec  (echo $l | awk -F'\t' '{print $3}')
    set def  (echo $l | awk -F'\t' '{print $4}')
    printf " %2d | %-20s | %-22s | %-7s | %s\n" $i $name $prov $rec $def >&2
    set i (math $i + 1)
  end

  set prefer (printf "%s\n" $lines | awk -F'\t' '$3=="Retain"{print $1}' | head -n1)
  if test -z "$prefer"
    set prefer (printf "%s\n" $lines | awk -F'\t' '$4=="true"{print $1}' | head -n1)
  end
  if test -z "$prefer"
    set prefer (printf "%s\n" $lines | awk -F'\t' '{print $1}' | head -n1)
  end

  read -P "Pick StorageClass by number or name (blank = $prefer): " choice

  if test -z "$choice"
    echo "Using StorageClass: $prefer" >&2
    echo $prefer
    return 0
  end

  if string match -qr '^[0-9]+$' -- $choice
    set idx (math $choice)
    if test $idx -lt 1 -o $idx -gt (count $lines)
      echo (set_color red)"Error:"(set_color normal)" Choice out of range." >&2
      return 1
    end
    set sel (printf "%s\n" $lines[$idx] | awk -F'\t' '{print $1}')
    echo "Using StorageClass: $sel" >&2
    echo $sel
    return 0
  end

  set found (printf "%s\n" $lines | awk -F'\t' -v want="$choice" '$1==want{print $1}')
  if test -z "$found"
    echo (set_color red)"Error:"(set_color normal)" No StorageClass named '$choice'." >&2
    return 1
  end
  echo "Using StorageClass: $found" >&2
  echo $found
end

# ---------- port-forward helpers (stderr logs) ----------
function _pf_start --description "start port-forward in bg: sets global PF_PID" -a NS SVC LOCAL REMOTE
  set -g PF_PID ""
  if test -z "$LOCAL"; set LOCAL 18083; end
  if test -z "$REMOTE"; set REMOTE 18083; end
  echo "Starting port-forward: svc/$SVC $LOCAL:$REMOTE (ns=$NS)…" >&2
  kubectl -n $NS port-forward svc/$SVC $LOCAL:$REMOTE >/dev/null 2>&1 &
  set -g PF_PID $last_pid
  sleep 1
end

function _pf_stop --description "stop port-forward if running"
  if test -n "$PF_PID"
    echo "Stopping port-forward (pid $PF_PID)…" >&2
    kill $PF_PID 2>/dev/null; or true
    set -e PF_PID
  end
end

# Minimal JSON-RPC caller for wallet RPC
function _rpc_call --description "call wallet JSON-RPC via localhost pf" -a RPCUSER RPCPASS METHOD PARAMS
  set url "http://127.0.0.1:18083/json_rpc"
  if test -z "$PARAMS"
    set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s"}' $METHOD)
  else
    set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s","params":%s}' $METHOD "$PARAMS")
  end
  curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
       -H "Content-Type: application/json" \
       -d "$payload" "$url"
end

# Probe a monerod and assert it looks like Monero mainnet (not test/stage), not "qubic".
# Usage: _probe_monerod host:port
function _probe_monerod -a ADDR
  if test -z "$ADDR"
    echo (set_color red)"Error:"(set_color normal)" _probe_monerod needs host:port" >&2
    return 1
  end

  if string match -qi '*qubic*' -- $ADDR
    echo (set_color red)"Refusing daemon '$ADDR' (contains 'qubic'). This is NOT a Monero node."(set_color normal) >&2
    return 1
  end

  # Try up to 3 short attempts
  for t in (seq 1 3)
    set ok 0
    set raw (curl -s --max-time 7 "http://$ADDR/get_info" 2>/dev/null)
    if test -n "$raw"
      set mstatus  (echo $raw | jq -r '.status // "OK"')
      set testnet  (echo $raw | jq -r '.testnet // false')
      set stagenet (echo $raw | jq -r '.stagenet // false')
      set height   (echo $raw | jq -r '.height // .target_height // 0')
      if test "$mstatus" = "OK"
        set ok 1
      end
    end

    if test $ok -eq 0
      set raw (curl -s --max-time 7 -H 'Content-Type: application/json' \
                   -d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' "http://$ADDR/json_rpc" \
                   | jq -r '.result? | @json' 2>/dev/null)
      if test -n "$raw"
        set mstatus  (echo $raw | jq -r '.status // "OK"')
        set testnet  (echo $raw | jq -r '.testnet // false')
        set stagenet (echo $raw | jq -r '.stagenet // false')
        set height   (echo $raw | jq -r '.height // .target_height // 0')
        if test "$mstatus" = "OK"
          set ok 1
        end
      end
    end

    if test $ok -eq 1
      if test "$testnet" = "true" -o "$stagenet" = "true"
        echo (set_color red)"Refusing non-mainnet daemon (testnet=$testnet stagenet=$stagenet) at $ADDR."(set_color normal) >&2
        return 1
      end
      echo "Daemon OK: Monero mainnet, height=$height"
      return 0
    end
    sleep 1
  end

  echo (set_color red)"Could not speak Monero /get_info to $ADDR. Not a monerod (or blocked)."(set_color normal) >&2
  echo "Tip: run your own monerod or set xmrwallet_SKIP_DAEMON_CHECK=1 to bypass." >&2
  return 1
end

# Wait for wallet RPC to accept connections (digest auth get_version)
function xmrwallet_wait_ready -a NS SVC SECS
  if test -z "$NS";  set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$SECS"; set SECS 60; end

  set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d 2>/dev/null)
  set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d 2>/dev/null)
  if test -z "$RPCUSER" -o -z "$RPCPASS"
    echo (set_color red)"Error:"(set_color normal)" missing RPC creds in secret {$SVC}-rpc-auth" >&2
    return 1
  end

  _pf_start $NS $SVC 18083 18083
  set tries (math "ceil($SECS/2)")
  for i in (seq 1 $tries)
    set code (curl -s -o /dev/null -w "%{http_code}" --digest -u "$RPCUSER:$RPCPASS" \
                -H 'Content-Type: application/json' \
                -d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' \
                http://127.0.0.1:18083/json_rpc)
    if test "$code" = "200"
      _pf_stop
      return 0
    end
    sleep 2
  end
  _pf_stop
  echo (set_color red)"RPC not ready after $SECS seconds."(set_color normal) >&2
  return 1
end

### ------- main workflow ---------------------------------------------------

function xmrwallet_bootstrap --description "Interactive setup of monero-wallet-rpc with PVC+secrets; creates default wallet and prints recovery info"
  _need kubectl jq curl awk; or return 1

  _banner "Monero wallet RPC bootstrap"

  # --- Namespace
  read -P "Namespace [crypto]: " ns_raw
  if test -z "$ns_raw"; set ns_raw crypto; end
  set ns (_k8s_name $ns_raw)
  _require "Namespace" $ns; or return 1

  echo "Ensuring namespace '$ns' exists…"
  if kubectl get ns $ns >/dev/null 2>&1
    echo "Namespace $ns already exists."
  else
    kubectl create ns $ns; or return 1
    echo "Created namespace $ns."
  end

  # --- StorageClass
  kubectl get storageclass
  echo "TIP: Prefer a class with reclaimPolicy=Retain so a PVC delete won't delete the PV."
  set sc (_choose_sc); or return 1

  # --- Unified name (prefix wallet-monero-)
  read -P "Wallet name (no spaces, e.g. 'brad', base will be wallet-monero-brad): " wallet_raw
  _require "Wallet name" $wallet_raw; or return 1
  if string match -rq '\s' -- $wallet_raw
    echo (set_color red)"Error:"(set_color normal)" Wallet name cannot contain spaces." >&2
    return 1
  end
  set base (_k8s_name $wallet_raw)
  set base (string replace -r '^wallet-monero-' '' -- $base)
  set name "wallet-monero-$base"

  # Bind all k8s object names to the unified base
  set pvc_name $name
  set app_name $name
  set svc_name $name
  set rpc_secret_name   "$app_name-rpc-auth"
  set wpass_secret_name "$app_name-wallet-pass"

  # --- Detect existing resources (all of them)
  set exists_any 0
  set found_list
  for r in "deploy/$app_name" "svc/$svc_name" "netpol/{$app_name}-ingress"
    if kubectl -n $ns get $r >/dev/null 2>&1
      set -a found_list $r
      set exists_any 1
    end
  end
  if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
    set -a found_list "pvc/$pvc_name"
    set exists_any 1
  end
  if kubectl -n $ns get secret $rpc_secret_name >/dev/null 2>&1
    set -a found_list "secret/$rpc_secret_name"
    set exists_any 1
  end
  if kubectl -n $ns get secret $wpass_secret_name >/dev/null 2>&1
    set -a found_list "secret/$wpass_secret_name"
    set exists_any 1
  end

  if test $exists_any -eq 1
    _banner "Existing resources detected for $name"
    for r in $found_list
      echo "  - $ns/$r"
    end
    read -P "Remove EVERYTHING (deploy/svc/netpol/PVC/SECRETS) and recreate? (y/N): " wipe
    if string match -qi 'y*' -- $wipe
      kubectl -n $ns delete deploy $app_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
      kubectl -n $ns delete svc    $svc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
      kubectl -n $ns delete netpol {$app_name}-ingress --ignore-not-found --wait=true >/dev/null 2>&1; or true
      kubectl -n $ns delete pvc    $pvc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
      kubectl -n $ns delete secret $rpc_secret_name --ignore-not-found >/dev/null 2>&1; or true
      kubectl -n $ns delete secret $wpass_secret_name --ignore-not-found >/dev/null 2>&1; or true
      echo "Cleaned up all previous resources."
    else
      echo "Reusing existing resources; they will be applied in-place."
    end
  end

  # --- Node scheduling (worker-only by default)
  read -P "Schedule only on worker nodes? (y/N) [y]: " pin_workers
  if test -z "$pin_workers"; set pin_workers y; end
  set use_pin 0
  if string match -qi 'y*' -- $pin_workers
    set use_pin 1
    read -P "Worker label key [node-role.kubernetes.io/worker]: " node_label_key
    if test -z "$node_label_key"; set node_label_key node-role.kubernetes.io/worker; end
    read -P "Worker label value [true]: " node_label_val
    if test -z "$node_label_val"; set node_label_val true; end
  end

  # --- Daemon (auto-use local monerod)
  echo
  echo "Monero daemon: using your cluster-local monerod by default."
  if test "$ns" = "crypto"
    set daemon_addr monerod:18081
  else
    set daemon_addr monerod.crypto.svc.cluster.local:18081
  end
  echo "Daemon address (for the Deployment): $daemon_addr"
  read -P "Override daemon address for this deployment? (host:port, blank to keep): " daemon_override
  if test -n "$daemon_override"
    set daemon_addr $daemon_override
  end

  if test -z "$xmrwallet_SKIP_DAEMON_CHECK"
    _banner "Probing daemon via temporary port-forward"
    set PF_LOCAL 28081
    _pf_start crypto monerod $PF_LOCAL 18081
    if not _probe_monerod 127.0.0.1:$PF_LOCAL
      _pf_stop
      return 1
    end
    _pf_stop
  else
    echo "Skipping daemon probe due to xmrwallet_SKIP_DAEMON_CHECK=1"
  end

  # Use your private image by default (in Harbor)
  read -P "Container image for wallet RPC [registry.bstein.dev/crypto/monero-wallet-rpc:0.18.4.1]: " image
  if test -z "$image"; set image registry.bstein.dev/crypto/monero-wallet-rpc:0.18.4.1; end
  _require "Container image" $image; or return 1

  # --- Secrets (defaults: RPC user=wallet name, passwords auto if missing)
  set existing_rpc_user (kubectl -n $ns get secret $rpc_secret_name -o jsonpath='{.data.username}' 2>/dev/null | base64 -d 2>/dev/null)
  set existing_rpc_pass (kubectl -n $ns get secret $rpc_secret_name -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null)
  set existing_wpass    (kubectl -n $ns get secret $wpass_secret_name -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null)

  set rpc_user_default $base
  if test -n "$existing_rpc_user"
    set rpc_user_default $existing_rpc_user
  end
  read -P "RPC username [$rpc_user_default]: " rpc_user
  if test -z "$rpc_user"; set rpc_user $rpc_user_default; end
  _require "RPC username" $rpc_user; or return 1

  read -s -P "RPC password [leave blank to keep current or auto-generate if none]: " rpc_pass
  echo
  if test -z "$rpc_pass"
    if test -n "$existing_rpc_pass"
      set rpc_pass $existing_rpc_pass
    else
      set rpc_pass (_rand_alnum32)
    end
  end

  read -P "Wallet filename (no path) [main]: " wallet_file_raw
  if test -z "$wallet_file_raw"; set wallet_file_raw main; end
  set wallet_file (_k8s_name $wallet_file_raw)
  _require "Wallet filename" $wallet_file; or return 1

  read -s -P "Wallet password [leave blank to keep current or auto-generate if none]: " wallet_pass
  echo
  if test -z "$wallet_pass"
    if test -n "$existing_wpass"
      set wallet_pass $existing_wpass
    else
      set wallet_pass (_rand_alnum32)
    end
  end

  _banner "Summary"
  echo "  Namespace:          $ns"
  echo "  StorageClass:       $sc"
  echo "  Base name:          $name"
  echo "  PVC:                $pvc_name"
  echo "  App/Deployment:     $app_name"
  echo "  Service:            $svc_name"
  echo "  Daemon address:     $daemon_addr"
  echo "  Image:              $image"
  echo "  RPC user:           $rpc_user"
  echo "  Wallet file:        /data/$wallet_file"
  if test $use_pin -eq 1
    echo "  NodeSelector:       $node_label_key=$node_label_val"
  end
  read -P "Proceed? [y/N]: " proceed
  if not string match -qi 'y*' -- $proceed
    echo (set_color yellow)"Aborted by user at confirmation step."(set_color normal)
    return 1
  end

  _banner "Applying secrets"
  kubectl -n $ns create secret generic $rpc_secret_name \
    --from-literal=username="$rpc_user" \
    --from-literal=password="$rpc_pass" \
    --dry-run=client -o yaml | kubectl -n $ns apply -f -; or return 1

  kubectl -n $ns create secret generic $wpass_secret_name \
    --from-literal=password="$wallet_pass" \
    --dry-run=client -o yaml | kubectl -n $ns apply -f -; or return 1

  set ts (date -Is)
  kubectl -n $ns annotate secret $rpc_secret_name   xmrwallet.titan/update-ts="$ts" --overwrite >/dev/null 2>&1
  kubectl -n $ns annotate secret $wpass_secret_name xmrwallet.titan/update-ts="$ts" --overwrite >/dev/null 2>&1

  echo "RPC creds: user='$rpc_user'  pass='$rpc_pass'"
  echo "Wallet pass: $wallet_pass"

  _banner "Applying PVC"
  if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
    echo "PVC $ns/$pvc_name exists; keeping as-is."
  else
    set pvcfile (mktemp -t {$pvc_name}-pvc.XXXX.yaml)
    begin
      echo "apiVersion: v1"
      echo "kind: PersistentVolumeClaim"
      echo "metadata:"
      echo "  name: $pvc_name"
      echo "  namespace: $ns"
      echo "spec:"
      echo "  accessModes: [\"ReadWriteOnce\"]"
      echo "  storageClassName: $sc"
      echo "  resources:"
      echo "    requests:"
      echo "      storage: 5Gi"
    end > $pvcfile
    echo "--- PVC manifest ($pvcfile) ---"
    cat $pvcfile
    kubectl apply -f $pvcfile; or return 1
  end

  _banner "Applying Deployment"
  set dply (mktemp -t {$app_name}-deploy.XXXX.yaml)
  begin
    echo "apiVersion: apps/v1"
    echo "kind: Deployment"
    echo "metadata:"
    echo "  name: $app_name"
    echo "  namespace: $ns"
    echo "  labels: { app: $app_name }"
    echo "spec:"
    echo "  replicas: 1"
    echo "  strategy:"
    echo "    type: Recreate"
    echo "  selector:"
    echo "    matchLabels: { app: $app_name }"
    echo "  template:"
    echo "    metadata:"
    echo "      labels: { app: $app_name }"
    echo "    spec:"
    if test $use_pin -eq 1
      echo "      nodeSelector:"
      echo "        $node_label_key: \"$node_label_val\""
    end
    echo "      securityContext:"
    echo "        fsGroup: 1000"
    echo "        fsGroupChangePolicy: OnRootMismatch"
    echo "      initContainers:"
    echo "      - name: volume-permissions"
    echo "        image: busybox:1.36"
    echo "        command: [\"/bin/sh\",\"-lc\",\"chown :1000 /data && chmod 0770 /data\"]"
    echo "        securityContext:"
    echo "          runAsUser: 0"
    echo "        volumeMounts:"
    echo "        - name: data"
    echo "          mountPath: /data"
    echo "      containers:"
    echo "      - name: wallet-rpc"
    echo "        image: $image"
    echo "        imagePullPolicy: Always"
    echo "        command: [\"/bin/sh\",\"-lc\"]"
    echo "        args:"
    echo "          - |"
    echo "            RPCU=\$(cat /run/monero-secrets/rpc-user);"
    echo "            RPCP=\$(cat /run/monero-secrets/rpc-pass);"
    echo "            exec /usr/local/bin/monero-wallet-rpc \\"
    echo "              --wallet-dir /data \\"
    echo "              --daemon-address $daemon_addr \\"
    echo "              --rpc-bind-ip 0.0.0.0 --rpc-bind-port 18083 \\"
    echo "              --rpc-login \"\${RPCU}:\${RPCP}\" \\"
    echo "              --confirm-external-bind"
    echo "        ports:"
    echo "        - containerPort: 18083"
    echo "        volumeMounts:"
    echo "        - name: data"
    echo "          mountPath: /data"
    echo "        - name: rpc-auth"
    echo "          mountPath: /run/monero-secrets"
    echo "          readOnly: true"
    echo "        resources:"
    echo "          requests: { cpu: \"100m\", memory: \"128Mi\" }"
    echo "          limits:   { cpu: \"1\",    memory: \"512Mi\" }"
    echo "      volumes:"
    echo "      - name: data"
    echo "        persistentVolumeClaim: { claimName: $pvc_name }"
    echo "      - name: rpc-auth"
    echo "        secret:"
    echo "          secretName: $rpc_secret_name"
    echo "          items:"
    echo "          - key: username"
    echo "            path: rpc-user"
    echo "          - key: password"
    echo "            path: rpc-pass"
  end > $dply
  echo "--- Deployment manifest ($dply) ---"
  head -n 70 $dply
  echo "..."
  kubectl apply -f $dply; or return 1

  _banner "Applying Service"
  set svc (mktemp -t {$app_name}-svc.XXXX.yaml)
  begin
    echo "apiVersion: v1"
    echo "kind: Service"
    echo "metadata:"
    echo "  name: $svc_name"
    echo "  namespace: $ns"
    echo "  labels: { app: $app_name }"
    echo "spec:"
    echo "  type: ClusterIP"
    echo "  selector: { app: $app_name }"
    echo "  ports:"
    echo "  - name: rpc"
    echo "    port: 18083"
    echo "    targetPort: 18083"
  end > $svc
  echo "--- Service manifest ($svc) ---"
  cat $svc
  kubectl apply -f $svc; or return 1

  _banner "Applying NetworkPolicy (same-namespace-only)"
  set np (mktemp -t {$app_name}-netpol.XXXX.yaml)
  begin
    echo "apiVersion: networking.k8s.io/v1"
    echo "kind: NetworkPolicy"
    echo "metadata:"
    printf "  name: %s-ingress\n" $app_name
    echo "  namespace: $ns"
    echo "spec:"
    echo "  podSelector: { matchLabels: { app: $app_name } }"
    echo "  policyTypes: [\"Ingress\"]"
    echo "  ingress:"
    echo "  - from:"
    echo "    - namespaceSelector:"
    echo "        matchLabels:"
    echo "          kubernetes.io/metadata.name: $ns"
    echo "    ports:"
    echo "    - port: 18083"
    echo "      protocol: TCP"
  end > $np
  echo "--- NetworkPolicy manifest ($np) ---"
  cat $np
  kubectl apply -f $np; or return 1

  _banner "Ensuring pod picks up latest secrets"
  kubectl -n $ns rollout restart deploy/$app_name >/dev/null 2>&1; or true

  _banner "Waiting for rollout"
  if not kubectl -n $ns rollout status deploy/$app_name --timeout=180s
    echo (set_color red)"Rollout did not finish in time. Recent events:"(set_color normal) >&2
    kubectl -n $ns get events --sort-by=.lastTimestamp | tail -n 40
    echo (set_color yellow)"Hint:"(set_color normal)" if you see Multi-Attach for the PVC, delete any older pod."
    return 1
  end

  _banner "Waiting for wallet RPC readiness"
  xmrwallet_wait_ready $ns $svc_name 60; or return 1

  _banner "Creating/opening wallet via JSON-RPC"
  if not xmrwallet_check_write $ns $app_name >/dev/null
    echo (set_color red)"PVC is not writable; aborting."(set_color normal)
    return 1
  end

  # Always attempt create; if it exists, open it.
  if not xmrwallet_create_wallet $ns $svc_name $wallet_file $wallet_pass
    echo (set_color yellow)"create_wallet returned non-zero; attempting open anyway…"(set_color normal)
  end
  if not xmrwallet_open $ns $svc_name $wallet_file $wallet_pass
    echo (set_color red)"Failed to open wallet $wallet_file. If this wallet already exists, the on-disk password may differ from the Secret. Use xmrwallet_open with the correct password or xmrwallet_change_wallet_password."(set_color normal)
    return 1
  end

  # --- Gather summary data (address, seed if enabled)
  set PRIMARY (xmrwallet_primary_address $ns $svc_name 2>/dev/null)
  set NEWADDR (xmrwallet_new_address $ns $svc_name "bootstrap-deposit" 0 2>/dev/null | awk '/New address:/{print $3}' )
  set SEEDRAW (xmrwallet_show_seed $ns $svc_name 2>&1)
  set SEED (echo $SEEDRAW | awk '/Mnemonic seed:/,0{if($0!~/Mnemonic seed:/)print}' )

  _banner "Done"
  echo "Namespace: $ns"
  echo "Base name: $name"
  echo "Service (in-cluster): $svc_name:18083"
  echo "PVC: $pvc_name via StorageClass '$sc'"
  echo "RPC auth: user='$rpc_user'"
  echo "RPC pass: $rpc_pass"
  echo "Wallet pass: $wallet_pass"
  echo "Wallet file: /data/$wallet_file"
  echo "Primary address (acct 0):"
  echo "  $PRIMARY"
  if test -n "$NEWADDR"
    echo "Fresh deposit subaddress (acct 0):"
    echo "  $NEWADDR"
  end
  if string match -qi '*Mnemonic seed:*' -- $SEEDRAW
    echo (set_color yellow)"Mnemonic seed (WRITE THIS DOWN, OFFLINE):"(set_color normal)
    echo $SEED
  else
    echo "Mnemonic seed not retrievable via RPC (image may disable it)."
    echo "If needed and supported by your image: xmrwallet_show_seed $ns $svc_name"
  end
  echo
  echo "Access locally: xmrwallet_portforward $ns $svc_name   # then http://127.0.0.1:18083/json_rpc"
  echo "Quick overview: xmrwallet_overview   $ns $svc_name"
end



### ------- utilities -------------------------------------------------------

function xmrwallet_portforward --description "port-forward RPC to localhost:18083" -a NS SVC
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  echo "Forwarding $NS/$SVC → http://127.0.0.1:18083 (Ctrl+C to stop)…"
  kubectl -n $NS port-forward svc/$SVC 18083:18083
end

function xmrwallet_status --description "show pod/service status" -a NS APP
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  kubectl -n $NS get deploy,po,svc,netpol -l app=$APP -o wide
end

function xmrwallet_stop --description "stop the RPC but keep PVC + secrets" -a NS APP SVC
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  if test -z "$SVC"; set SVC $APP; end
  kubectl -n $NS delete deploy $APP --ignore-not-found
  kubectl -n $NS delete svc $SVC --ignore-not-found
  echo "Stopped $NS/$APP. PVC and secrets retained."
end

function xmrwallet_purge --description "IRREVERSIBLY delete PVC + secrets (keys on disk!)" -a NS APP PVC SVC
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  if test -z "$PVC"; set PVC wallet-data; end
  if test -z "$SVC"; set SVC $APP; end
  echo "Type to confirm: PURGE $NS $PVC"
  read CONFIRM
  if test "$CONFIRM" != "PURGE $NS $PVC"
    echo "Aborted."
    return 1
  end
  kubectl -n $NS delete deploy $APP --ignore-not-found
  kubectl -n $NS delete svc $SVC --ignore-not-found
  kubectl -n $NS delete pvc $PVC --ignore-not-found --wait=true
  kubectl -n $NS delete secret $APP-wallet-pass --ignore-not-found
  kubectl -n $NS delete secret $APP-rpc-auth --ignore-not-found
  echo "Purged. Ensure you have the mnemonic backed up if you ever need to restore."
end

function xmrwallet_show_seed --description "Try to fetch mnemonic via RPC query_key (wallet must be open)" -a NS SVC RPCUSER RPCPASS
  _need jq curl; or return 1
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$RPCUSER"
    set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
  end
  if test -z "$RPCPASS"
    set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d)
  end
  _pf_start $NS $SVC 18083 18083
  set res (_rpc_call $RPCUSER $RPCPASS query_key '{"key_type":"mnemonic"}')
  _pf_stop
  set err (echo $res | jq -r '.error.message // empty')
  if test -n "$err"
    echo "query_key error: $err"
    echo "Some images disable returning mnemonics via RPC. Consider backing up offline."
    return 1
  end
  echo "Mnemonic seed:"
  echo $res | jq -r '.result.key'
end

# ---------- core JSON-RPC wrapper (raw JSON to stdout) ----------
function xmrwallet_rpc_call --description "RPC call with secrets (auto port-forward)" -a NS SVC METHOD PARAMS_JSON
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$METHOD"
    echo "Usage: xmrwallet_rpc_call [ns] [svc] <method> [params_json]" >&2
    return 1
  end

  set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
  set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d)

  _pf_start $NS $SVC 18083 18083
  if test -n "$PARAMS_JSON"
    set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s","params":%s}' $METHOD "$PARAMS_JSON")
  else
    set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s"}' $METHOD)
  end

  set tmp (mktemp)
  set code (curl -s --digest -u "$RPCUSER:$RPCPASS" \
                -H 'Content-Type: application/json' \
                -o $tmp -w "%{http_code}" \
                -d "$payload" "http://127.0.0.1:18083/json_rpc")
  set rc $status
  _pf_stop

  set body (cat $tmp); rm -f $tmp

  if test $rc -ne 0
    echo "RPC transport error ($METHOD): curl exit $rc" >&2
    echo $body
    return $rc
  end

  if test "$code" != "200"
    echo "RPC HTTP $code ($METHOD)" >&2
    echo $body
    return 1
  end

  echo $body
  return 0
end

function xmrwallet_rpc_test --description "get_version using secrets" -a NS SVC
  xmrwallet_rpc_call $NS $SVC get_version | jq .
end

# Rotate RPC username/password (updates Secret & restarts Deploy)
function xmrwallet_set_rpc_credentials --description "rotate RPC Basic creds" -a NS APP
  _need kubectl; or return 1
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  set SNAME {$APP}-rpc-auth

  read -P "New RPC username [leave blank to keep current]: " NEWUSER
  if test -z "$NEWUSER"
    set NEWUSER (kubectl -n $NS get secret $SNAME -o jsonpath='{.data.username}' | base64 -d)
  end
  read -s -P "New RPC password [blank = auto-generate 32 alnum]: " NEWPASS
  echo
  if test -z "$NEWPASS"; set NEWPASS (_rand_alnum32); end

  echo "Updating secret $NS/$SNAME and restarting $APP…"
  kubectl -n $NS create secret generic $SNAME \
    --from-literal=username="$NEWUSER" \
    --from-literal=password="$NEWPASS" \
    --dry-run=client -o yaml | kubectl -n $NS apply -f -; or return 1
  kubectl -n $NS annotate secret $SNAME xmrwallet.titan/update-ts=(date -Is) --overwrite >/dev/null 2>&1
  kubectl -n $NS rollout restart deploy/$APP
  kubectl -n $NS rollout status deploy/$APP --timeout=180s; or return 1

  echo "New RPC creds:"
  echo "  user: $NEWUSER"
  echo "  pass: $NEWPASS"
end

# Change wallet file password via RPC
function xmrwallet_change_wallet_password --description "change password of a wallet file via RPC" -a NS SVC
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end

  read -P "Wallet filename (under /data): " WFILE
  if test -z "$WFILE"
    echo "Wallet filename required." >&2
    return 1
  end
  read -s -P "Current wallet password: " OLD
  echo
  read -s -P "New wallet password [blank = auto-generate 32 alnum]: " NEW
  echo
  if test -z "$NEW"; set NEW (_rand_alnum32); end

  set res (xmrwallet_rpc_call $NS $SVC open_wallet (printf '{"filename":"%s","password":"%s"}' $WFILE $OLD))
  xmrwallet_rpc_call $NS $SVC change_wallet_password (printf '{"old_password":"%s","new_password":"%s"}' $OLD $NEW)
  echo "New wallet password: $NEW"
end

# --- logs from the deployment (quick tail) ---
function xmrwallet_logs -a NS APP LINES
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  if test -z "$LINES"; set LINES 200; end
  kubectl -n $NS logs deploy/$APP --tail=$LINES
end

# --- list files in /data inside the pod ---
function xmrwallet_wallet_ls -a NS APP
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  echo "Listing /data in $NS/$APP…" >&2
  kubectl -n $NS exec deploy/$APP -- sh -lc 'ls -la /data || true'
end

# --- create a wallet (uses Secret password if PASS omitted) ---
function xmrwallet_create_wallet -a NS SVC FILE PASS LANG
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$FILE"
    echo "Usage: xmrwallet_create_wallet [ns] [svc] <filename> [pass] [language]" >&2
    return 1
  end
  if test -z "$PASS"
    set PASS (kubectl -n $NS get secret {$SVC}-wallet-pass -o jsonpath='{.data.password}' | base64 -d 2>/dev/null)
  end
  if test -z "$LANG"; set LANG English; end

  set payload (printf '{"filename":"%s","password":"%s","language":"%s"}' $FILE $PASS $LANG)
  set res (xmrwallet_rpc_call $NS $SVC create_wallet $payload)
  set rc $status
  if test $rc -ne 0
    echo (set_color red)"create_wallet RPC transport failed (rc=$rc)."(set_color normal) >&2
    return $rc
  end

  set err (echo $res | jq -r '.error.message // empty')
  if test -n "$err"
    if string match -q "*already exists*" -- $err
      echo "Wallet $FILE already exists."
      return 0
    end
    echo "create_wallet error: $err" >&2
    return 1
  end

  echo "Created wallet: $FILE"
end

# --- improved opener: closes first, checks files, hints if password mismatch ---
function xmrwallet_open -a NS SVC FILE PASS
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$FILE"
    echo "Usage: xmrwallet_open [ns] [svc] <filename> [password]" >&2
    return 1
  end
  if test -z "$PASS"
    set PASS (kubectl -n $NS get secret {$SVC}-wallet-pass -o jsonpath='{.data.password}' | base64 -d 2>/dev/null)
  end
  if test -z "$PASS"
    read -s -P "Wallet password for $FILE: " PASS; echo
  end

  xmrwallet_rpc_call $NS $SVC close_wallet >/dev/null 2>&1; or true

  set res (xmrwallet_rpc_call $NS $SVC open_wallet (printf '{"filename":"%s","password":"%s"}' $FILE $PASS))
  set rc $status
  if test $rc -ne 0
    echo (set_color red)"open_wallet RPC transport failed (rc=$rc)."(set_color normal) >&2
    return $rc
  end

  set exists (kubectl -n $NS exec deploy/$SVC -- sh -lc (printf 'test -e /data/%s -o -e /data/%s.keys && echo yes || echo no' $FILE $FILE) 2>/dev/null)
  if test "$exists" = "no"
    echo "No wallet files at /data/$FILE{,.keys}. If this should be a new wallet, run:" >&2
    echo "  xmrwallet_create_wallet $NS $SVC $FILE" >&2
    return 1
  end

  set tmp (mktemp)
  xmrwallet_rpc_call $NS $SVC open_wallet (printf '{"filename":"%s","password":"%s"}' $FILE $PASS) > $tmp
  set rc $status
  set res (cat $tmp); rm -f $tmp

  if test $rc -ne 0
    echo "open_wallet transport/HTTP error (rc=$rc)" >&2
    echo $res | jq . >&2
    return $rc
  end

  set err (echo $res | jq -r '.error.message // empty' 2>/dev/null)
  if test -n "$err"
    echo "open_wallet error: $err" >&2
    if string match -q "*password*" -- (string lower -- $err)
      echo "Hint: Secret {$SVC}-wallet-pass may not match the files on disk. Try supplying the pass explicitly." >&2
    end
    return 1
  end

  echo $res | jq .
end

function xmrwallet_check_write -a NS APP
  if test -z "$NS"; set NS crypto; end
  if test -z "$APP"; set APP wallet-rpc; end
  kubectl -n $NS exec deploy/$APP -- sh -lc 'echo test-$(date +%s) > /data/.write-test && ls -l /data/.write-test && rm -f /data/.write-test'
end

# --- ensure wallet is open (create if missing) ---
function xmrwallet_ensure_open -a NS SVC FILE
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$FILE"; set FILE main; end

  set exists (kubectl -n $NS exec deploy/$SVC -- sh -lc (printf 'if [ -e "/data/%s" ] || [ -e "/data/%s.keys" ]; then echo yes; else echo no; fi' $FILE $FILE) 2>/dev/null)
  if test "$exists" = "no"
    echo "No /data/$FILE found. Creating…" >&2
    xmrwallet_create_wallet $NS $SVC $FILE; or return 1
  end

  xmrwallet_open $NS $SVC $FILE; or return 1
end

# --- quick primary address print (account 0)
function xmrwallet_primary_address -a NS SVC
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  _pf_start $NS $SVC 18083 18083
  set resp (curl -s --fail --digest \
               -u (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d):(kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d) \
               -H 'Content-Type: application/json' \
               -d '{"jsonrpc":"2.0","id":"0","method":"get_address","params":{"account_index":0}}' \
               http://127.0.0.1:18083/json_rpc)
  _pf_stop
  echo $resp | jq -r '.result.address'
end

# --- show unlocked balance quickly
function xmrwallet_unlocked -a NS SVC
  _need python3; or return 1
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  set res (xmrwallet_rpc_call $NS $SVC get_balance '{"account_index":0}')
  set bal (echo $res | jq -r '.result.balance // 0')
  set unl (echo $res | jq -r '.result.unlocked_balance // 0')

  # Convert piconero -> XMR using Python (fish-safe)
  set balx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $bal)
  set unlx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $unl)

  echo "balance=$balx XMR  unlocked=$unlx XMR"
end

# --- wait until unlocked balance >= threshold (XMR), timeout seconds
function xmrwallet_wait_unlocked -a NS SVC XMR_MIN TIMEOUT
  _need python3; or return 1
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$XMR_MIN"; set XMR_MIN 0; end
  if test -z "$TIMEOUT"; set TIMEOUT 300; end
  set start (date +%s)
  while true
    set res (xmrwallet_rpc_call $NS $SVC get_balance '{"account_index":0}')
    set unl (echo $res | jq -r '.result.unlocked_balance // 0')
    set unlx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $unl)
    set ge (python3 -c 'from decimal import Decimal; import sys; print(1 if Decimal(sys.argv[1]) >= Decimal(sys.argv[2]) else 0)' -- $unlx $XMR_MIN)
    if test $ge -eq 1
      echo "Unlocked: $unlx XMR (≥ $XMR_MIN)"
      return 0
    end
    if test (math (date +%s) - $start) -ge $TIMEOUT
      echo "Timed out waiting for unlocked balance ≥ $XMR_MIN XMR (current $unlx XMR)."
      return 1
    end
    sleep 6
  end
end

# ---------- friendly overview ----------
function xmrwallet_overview -a NS SVC ACCOUNT
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$ACCOUNT"; set ACCOUNT 0; end

  set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
  set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d)

  _pf_start $NS $SVC 18083 18083
  echo "== Wallet RPC overview (ns=$NS svc=$SVC acct=$ACCOUNT)"

  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' \
                 http://127.0.0.1:18083/json_rpc)
  echo $resp | jq -r '"-- Version:\n  version=\(.result.version)  release=\(.result.release)"'

  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d '{"jsonrpc":"2.0","id":"0","method":"get_height"}' \
                 http://127.0.0.1:18083/json_rpc)
  set err (echo $resp | jq -r '.error.message // empty')
  echo "-- Height:"
  if test -n "$err"
    echo "  ERROR: $err"
    echo "  (Tip: open your wallet: xmrwallet_open $NS $SVC main)"
  else
    echo $resp | jq -r '"  wallet_height=\(.result.height)"'
  end

  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d '{"jsonrpc":"2.0","id":"0","method":"get_accounts"}' \
                 http://127.0.0.1:18083/json_rpc)
  set err (echo $resp | jq -r '.error.message // empty')
  echo "-- Accounts:"
  if test -n "$err"
    echo "  ERROR: $err"
  else
    echo $resp | jq -r '.result.subaddress_accounts[]
      | "  [\(.account_index)] \(.label)  total=\(.balance/1000000000000) XMR  unlocked=\(.unlocked_balance/1000000000000) XMR"'
  end

  set BODY (printf '{"account_index":%s}' $ACCOUNT)
  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d (printf '{"jsonrpc":"2.0","id":"0","method":"get_balance","params":%s}' "$BODY") \
                 http://127.0.0.1:18083/json_rpc)
  set err (echo $resp | jq -r '.error.message // empty')
  echo "-- Account balance:"
  if test -n "$err"
    echo "  ERROR: $err"
  else
    echo $resp | jq -r '"  balance=\(.result.balance/1000000000000) XMR  unlocked=\(.result.unlocked_balance/1000000000000) XMR"'
  end

  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d (printf '{"jsonrpc":"2.0","id":"0","method":"get_address","params":{"account_index":%s}}' $ACCOUNT) \
                 http://127.0.0.1:18083/json_rpc)
  set err (echo $resp | jq -r '.error.message // empty')
  echo "-- Primary address (account base):"
  if test -n "$err"
    echo "  ERROR: $err"
  else
    echo $resp | jq -r '.result.address'
  end

  echo "-- First 5 subaddresses:"
  if test -n "$err"
    echo "  (skipped due to error above)"
  else
    echo $resp | jq -r '.result.addresses[:5][] | "  [\(.address_index)] \(.address)  label=\(.label)  used=\(.used)"'
  end

  set BODY (printf '{"account_index":%s,"in":true}' $ACCOUNT)
  set in (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
             -H 'Content-Type: application/json' \
             -d (printf '{"jsonrpc":"2.0","id":"0","method":"get_transfers","params":%s}' "$BODY") \
             http://127.0.0.1:18083/json_rpc)
  echo "-- Recent transfers (incoming, last 5):"
  echo $in | jq -r '.result.in // [] | (.[-5:] // [])[] | "  + \(.amount/1000000000000) XMR  conf=\(.confirmations)  tx=\(.txid)"'

  set BODY (printf '{"account_index":%s,"out":true}' $ACCOUNT)
  set out (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
              -H 'Content-Type: application/json' \
              -d (printf '{"jsonrpc":"2.0","id":"0","method":"get_transfers","params":%s}' "$BODY") \
              http://127.0.0.1:18083/json_rpc)
  echo "-- Recent transfers (outgoing, last 5):"
  echo $out | jq -r '.result.out // [] | (.[-5:] // [])[] | "  - \(.amount/1000000000000) XMR  conf=\(.confirmations)  tx=\(.txid)"'

  set BODY (printf '{"account_index":%s,"pool":true}' $ACCOUNT)
  set pool (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
               -H 'Content-Type: application/json' \
               -d (printf '{"jsonrpc":"2.0","id":"0","method":"get_transfers","params":%s}' "$BODY") \
               http://127.0.0.1:18083/json_rpc)
  echo "-- Mempool (unconfirmed incoming):"
  echo $pool | jq -r '.result.pool // [] | .[] | "  ~ \(.amount/1000000000000) XMR  tx=\(.txid)"'

  _pf_stop
end

# ---------- list addresses ----------
function xmrwallet_list_addresses -a NS SVC ACCOUNT
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$ACCOUNT"; set ACCOUNT 0; end
  set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
  set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d)
  _pf_start $NS $SVC 18083 18083
  set BODY (printf '{"account_index":%s}' $ACCOUNT)
  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d (printf '{"jsonrpc":"2.0","id":"0","method":"get_address","params":%s}' "$BODY") \
                 http://127.0.0.1:18083/json_rpc)
  _pf_stop
  set err (echo $resp | jq -r '.error.message // empty')
  if test -n "$err"
    echo "ERROR: $err" >&2
    return 1
  end
  echo $resp | jq -r '.result.addresses[] | "[\(.address_index)] \(.address)  label=\(.label)  used=\(.used)"'
end

# ---------- new subaddress ----------
function xmrwallet_new_address -a NS SVC LABEL ACCOUNT
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$ACCOUNT"; set ACCOUNT 0; end
  if test -z "$LABEL"; set LABEL (printf "deposit-%s" (date +%Y%m%d-%H%M%S)); end
  set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
  set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d)
  _pf_start $NS $SVC 18083 18083
  set BODY (printf '{"account_index":%s,"label":"%s"}' $ACCOUNT $LABEL)
  set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
                 -H 'Content-Type: application/json' \
                 -d (printf '{"jsonrpc":"2.0","id":"0","method":"create_address","params":%s}' "$BODY") \
                 http://127.0.0.1:18083/json_rpc)
  _pf_stop
  set err (echo $resp | jq -r '.error.message // empty')
  if test -n "$err"
    echo "ERROR: $err" >&2
    return 1
  end
  echo $resp | jq -r --arg L "$LABEL" '"New address: \(.result.address)  [index=\(.result.address_index)]  label=" + $L'
end

# --- send a specific amount XMR to a single address (optionally payment_id)
# Usage: xmrwallet_send [ns] [svc] <to_address> <amount_xmr> [payment_id_hex]
function xmrwallet_send -a NS SVC TO AMT_XMR PID
  _need jq curl python3; or return 1
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$TO" -o -z "$AMT_XMR"
    echo "Usage: xmrwallet_send [ns] [svc] <to_address> <amount_xmr> [payment_id_hex]" >&2
    return 1
  end

  # Convert amount to atomic units
  set ATOMIC (_xmr_to_atomic $AMT_XMR)
  if test -z "$ATOMIC"
    echo "Failed to convert amount '$AMT_XMR' to piconero." >&2
    return 1
  end

  # Build JSON
  set body_base (printf '{"destinations":[{"amount":%s,"address":"%s"}],"account_index":0,"priority":1,"get_tx_key":true,"do_not_relay":false}' $ATOMIC $TO)
  if test -n "$PID"
    set body (string replace '}' ',"payment_id":"'$PID'"}' -- $body_base)
  else
    set body $body_base
  end

  set res (xmrwallet_rpc_call $NS $SVC transfer $body)
  set err (echo $res | jq -r '.error.message // empty')
  if test -n "$err"
    echo "ERROR: $err" >&2
    return 1
  end

  set fee (echo $res | jq -r '.result.fee // 0')
  set tx  (echo $res | jq -r '.result.tx_hash // (.result.tx_hash_list[0] // "")')

  # piconero -> XMR without heredoc
  set fee_xmr (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $fee)

  echo "Sent transaction:"
  echo "  tx:  $tx"
  echo "  fee: $fee_xmr XMR ($fee piconero)"
end

# --- sweep all unlocked funds to an address (account 0)
# Usage: xmrwallet_sweep_all [ns] [svc] <to_address>
function xmrwallet_sweep_all -a NS SVC TO
  _need jq curl; or return 1
  if test -z "$NS"; set NS crypto; end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$TO"
    echo "Usage: xmrwallet_sweep_all [ns] [svc] <to_address>" >&2
    return 1
  end

  set body (printf '{"address":"%s","account_index":0,"priority":1,"do_not_relay":false,"get_tx_keys":true}' $TO)
  set res (xmrwallet_rpc_call $NS $SVC sweep_all $body)
  set err (echo $res | jq -r '.error.message // empty')
  if test -n "$err"
    echo "ERROR: $err" >&2
    return 1
  end

  echo "Sweep transactions:"
  echo $res | jq -r '.result.tx_hash_list[]? | "  tx: " + .'
end

# Return total size (bytes) of /data/<file> + /data/<file>.keys inside the RPC pod
# Usage: xmrwallet_wallet_size_bytes [ns] [app] [file]
function xmrwallet_wallet_size_bytes -a NS APP FILE
  if test -z "$NS";   set NS crypto; end
  if test -z "$APP";  set APP wallet-rpc; end
  if test -z "$FILE"; set FILE main; end
  set CMD (printf 'f="/data/%s"; k="/data/%s.keys"; s=0; [ -e "$f" ] && s=$(stat -c %%s "$f"); s2=0; [ -e "$k" ] && s2=$(stat -c %%s "$k"); echo $((s+s2))' $FILE $FILE)
  set SUM (kubectl -n $NS exec deploy/$APP -- sh -lc "$CMD" 2>/dev/null)
  if test -z "$SUM"; set SUM 0; end
  echo $SUM
end

# Prometheus-friendly metric line (total bytes)
# Usage: xmrwallet_metrics [ns] [app] [file]
function xmrwallet_metrics -a NS APP FILE
  if test -z "$NS";   set NS crypto; end
  if test -z "$APP";  set APP wallet-rpc; end
  if test -z "$FILE"; set FILE main; end
  set BYTES (xmrwallet_wallet_size_bytes $NS $APP $FILE)
  printf "wallet_size_bytes{chain=\"monero\",namespace=\"%s\",app=\"%s\",wallet=\"%s\"} %s\n" $NS $APP $FILE $BYTES
end

# Restore a wallet file via wallet-RPC from a 25-word seed
# Usage: xmrwallet_restore [ns] [svc] [wallet_file] [restore_height] [language=English]
function xmrwallet_restore -a NS SVC FILE RH LANG
  _need jq curl; or return 1
  if test -z "$NS";  set NS crypto;    end
  if test -z "$SVC"; set SVC wallet-rpc; end
  if test -z "$FILE"; set FILE main;   end
  if test -z "$RH";   set RH 0;        end
  if test -z "$LANG"; set LANG English; end

  # Get RPC creds and prompt for wallet password + seed (hidden)
  set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
  set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d)

  echo (set_color yellow)"New wallet password (input hidden):"(set_color normal)
  read -s WPASS; echo
  if test -z "$WPASS"
    echo (set_color red)"Error:"(set_color normal)" empty wallet password."; return 1
  end

  echo (set_color yellow)"Paste your 25-word Monero seed (input hidden):"(set_color normal)
  read -s SEED; echo
  if test -z "$SEED"
    echo (set_color red)"Error:"(set_color normal)" empty seed."; return 1
  end

  # Open a port-forward for the RPC, call restore_deterministic_wallet
  function _pf_start_local
    kubectl -n $NS port-forward svc/$SVC 18083:18083 >/dev/null 2>&1 &
    set -g PF_PID $last_pid
    sleep 1
  end
  function _pf_stop_local
    if test -n "$PF_PID"; kill $PF_PID 2>/dev/null; set -e PF_PID; end
  end

  _pf_start_local
  set body (printf '{"seed":"%s","password":"%s","filename":"%s","language":"%s","restore_height":%s}' $SEED $WPASS $FILE $LANG $RH)
  set resp (curl -s --digest -u "$RPCUSER:$RPCPASS" -H 'Content-Type: application/json' \
               -d (printf '{"jsonrpc":"2.0","id":"0","method":"restore_deterministic_wallet","params":%s}' "$body") \
               http://127.0.0.1:18083/json_rpc)
  _pf_stop_local

  set err (echo $resp | jq -r '.error.message // empty')
  if test -n "$err"
    echo (set_color red)"restore_deterministic_wallet error:"(set_color normal) $err >&2
    return 1
  end

  echo "Restored wallet /data/$FILE (from seed). Beginning blockchain sync…"
  echo (set_color yellow)"Keep your seed offline and secure."(set_color normal)
end

function cluster_probe
  set OUT cluster-snapshot.txt
  printf "# Cluster snapshot %s\n" (date -Is) > $OUT

  printf "\n## kubectl version\n" >> $OUT
  kubectl version --short >> $OUT 2>&1

  printf "\n## Nodes\n" >> $OUT
  kubectl get nodes -o wide >> $OUT

  printf "\n### Node resources\n" >> $OUT
  kubectl describe nodes | egrep -i 'Name:|Roles:|Capacity:|Allocatable:|cpu|memory|ephemeral' >> $OUT

  printf "\n## Namespaces\n" >> $OUT
  kubectl get ns >> $OUT

  printf "\n## StorageClasses (summary)\n" >> $OUT
  kubectl get storageclass >> $OUT
  printf "\n## StorageClasses (yaml)\n" >> $OUT
  kubectl get storageclass -o yaml >> $OUT

  printf "\n## PV/PVC\n" >> $OUT
  kubectl get pv >> $OUT 2>/dev/null
  kubectl get pvc -A >> $OUT 2>/dev/null

  printf "\n## Ingress / Services\n" >> $OUT
  kubectl get ingressclass >> $OUT 2>/dev/null
  kubectl get ingress -A >> $OUT 2>/dev/null
  kubectl get svc -A | egrep -i 'NAMESPACE|LoadBalancer|EXTERNAL-IP|traefik|ingress' >> $OUT

  printf "\n## kube-system pods (CNI hint)\n" >> $OUT
  kubectl -n kube-system get pods -o wide >> $OUT 2>/dev/null

  printf "\n## Default limits/quotas\n" >> $OUT
  kubectl -n default get limitrange,resourcequota >> $OUT 2>/dev/null

  printf "\n## Tools present on your machine\n" >> $OUT
  for t in kubectl helm jq curl awk envsubst fish
    if type -q $t
      printf "%s: OK\n" $t >> $OUT
    else
      printf "%s: MISSING\n" $t >> $OUT
    end
  end

  echo "Wrote $OUT — paste the contents here."
end

# --- one-line command reference (keep in sync) ---
function xmrwallet_help
  echo "xmrwallet_bootstrap                # interactive deploy + secrets + PVC + first wallet (multiwallet RPC), idempotent"
  echo "xmrwallet_status [ns] [app]        # show deploy, pods, service, netpol (default ns=crypto)"
  echo "xmrwallet_portforward [ns] [svc]   # port-forward RPC to localhost:18083 (CTRL+C to stop)"
  echo "xmrwallet_logs [ns] [app] [N]      # tail N (default 200) lines of RPC pod logs"
  echo "xmrwallet_wallet_ls [ns] [app]     # list files under /data in the RPC pod (check wallet files)"
  echo "xmrwallet_rpc_test [ns] [svc]      # JSON-RPC get_version (sanity check)"
  echo "xmrwallet_rpc_call [ns] [svc] <method> [params_json]   # raw JSON (stdout) with digest auth"
  echo "xmrwallet_open [ns] [svc] <file> [pass]                # close_wallet (ignore) -> open_wallet"
  echo "xmrwallet_create_wallet [ns] [svc] <file> [pass] [lang]# create wallet (defaults: pass from Secret, lang=English)"
  echo "xmrwallet_ensure_open [ns] [svc] [file]                # create if missing, then open (uses Secret pass)"
  echo "xmrwallet_overview [ns] [svc] [acct=0] # version, height, balances, a few addresses, recent transfers"
  echo "xmrwallet_list_addresses [ns] [svc] [acct=0]           # list all subaddresses for account"
  echo "xmrwallet_new_address [ns] [svc] [label] [acct=0]      # create labeled subaddress"
  echo "xmrwallet_set_rpc_credentials [ns] [app]               # rotate RPC basic auth (secret + rollout)"
  echo "xmrwallet_change_wallet_password [ns] [svc]            # change wallet file password via RPC"
  echo "xmrwallet_stop [ns] [app] [svc]                        # delete deployment+service only"
  echo "xmrwallet_purge [ns] [app] [pvc] [svc]                 # delete deploy+svc+PVC+secrets (danger!)"
end

function xmrwallet_help_detailed
  echo "Commands (defaults: ns=crypto, svc/app=wallet-rpc)"
  echo
  echo "xmrwallet_bootstrap"
  echo "  Interactive deploy: creates/updates secrets, PVC, Deployment (fsGroup+init perms), Service, NetPolicy."
  echo "  Idempotent: if deploy/svc/netpol already exist, you can clean them up (PVC+secrets kept) and continue."
  echo "  Uses your in-cluster monerod by default (monerod:18081 in ns=crypto)."
  echo "  Probes it via a temporary port-forward so it works from your workstation."
  echo "  Set xmrwallet_SKIP_DAEMON_CHECK=1 to bypass the daemon probe (not recommended)."
end
