#!/usr/bin/env bash
# ============================================================================
# hyve-carryover  —  lossless migration of a HYVE Ether OS install to new HW
# ============================================================================
# HYVE activation is 100% OFFLINE and NOT hardware-bound, so the *license*
# transfers for free (re-enter the key on the new box, or copy activation.json).
# The only thing that must actually be MOVED is the learned state: Omega's
# world-model + memory, the customer's grown/distilled models, skills, the
# tamper-evident ledger, and the per-install crypto identity.
#
# This tool packages ALL of that into a single (optionally encrypted) capsule on
# the old machine and restores it byte-for-byte on the new one.
#
#   OLD box:  hyve-carryover export  [--lean] [--encrypt]
#   NEW box:  hyve-carryover import  <capsule>
#   (no args) interactive wizard
#
# Design principle: copy the WHOLE state tree minus a short, explicit exclude
# list of regenerable/telemetry/cache. Enumerating what to KEEP risks silently
# dropping a subsystem; enumerating what to DROP does not.
# ============================================================================
set -euo pipefail
VERSION="1.0.0"

HYVE="$HOME/.hyve"
ASCENT="$HOME/hyve-ascent"
SHELL_DATA="$HOME/.local/share/co.hyveapp.etheros.shell"
SHELL_CFG="$HOME/.config/co.hyveapp.etheros.shell"

# Locally-built custom models (FROM a local blob — `ollama pull` cannot recreate
# these, so their blobs+manifests must travel in the capsule).
CUSTOM_MODELS=(omega:latest apprentice:latest apprentice-cmdsafety:latest)

# Regenerable / telemetry / cache / dev-only — safe to drop (rebuilt on the new
# box or pure log noise). Paths are ANCHORED to their archive root so a nested
# name like omega/generations.jsonl is matched exactly (a bare pattern would not
# match ".hyve/omega/generations.jsonl"). NOTHING learned/identity is dropped.
EXCLUDES=(
  --exclude='*.log'
  --exclude='*.bak-*' --exclude='__pycache__' --exclude='.venv'
  --exclude='*/WebKitCache' --exclude='*/CacheStorage' --exclude='*/GPUCache'
  --exclude='.hyve/hardware.json'                 # hwtier regenerates for new HW
  --exclude='.hyve/models.env'                    # hwtier regenerates for new HW
  --exclude='.hyve/version'                        # re-stamped by installer
  --exclude='.hyve/geoip'                          # 125M re-downloadable static db
  --exclude='.hyve/updates'                        # re-fetchable update pkgs
  --exclude='.hyve/prompts/library-backup-*'
  --exclude='.hyve/prompts/library-presanitize-bak'
  --exclude='.hyve/spy/tiles'                      # map-tile cache
  --exclude='.hyve/oracle/giant_prompt.cache'
  --exclude='.hyve/omega/generations.jsonl'        # replayable telemetry
  --exclude='.hyve/omega/llm-calls.jsonl'          # raw call telemetry
)
# --lean additionally drops large user *output* (keeps brain+identity, drops media)
LEAN_EXCLUDES=( --exclude='.hyve/cinema' --exclude='.hyve/spark' --exclude='.hyve/pulse' )

c(){ printf '\033[1;35m[carryover]\033[0m %s\n' "$*"; }
die(){ printf '\033[1;31m[carryover] ERROR:\033[0m %s\n' "$*" >&2; exit 1; }

ollama_models_dir(){
  if [[ -n "${OLLAMA_MODELS:-}" && -d "$OLLAMA_MODELS" ]]; then echo "$OLLAMA_MODELS"; return; fi
  for d in /usr/share/ollama/.ollama/models "$HOME/.ollama/models"; do
    [[ -d "$d" ]] && { echo "$d"; return; }
  done
  echo ""
}

# --------------------------------------------------------------------------
export_capsule(){
  local lean=0 encrypt=0 a
  for a in "$@"; do
    [[ "$a" == "--lean" ]] && lean=1
    [[ "$a" == "--encrypt" ]] && encrypt=1
  done

  local stamp; stamp="$(date -u +%Y%m%dT%H%M%SZ)"
  local work; work="$(mktemp -d)"
  local out="$PWD/hyve-carryover-${stamp}.hyvecapsule"
  c "staging capsule in $work"

  # 1) State tree (whole ~/.hyve + ~/hyve-ascent + shell profile, minus excludes).
  #    -p preserves the 0600/0400 modes on secrets + ledger segments.
  local roots=()
  [[ -d "$HYVE" ]]       && roots+=( ".hyve" )
  [[ -d "$ASCENT" ]]     && roots+=( "hyve-ascent" )
  [[ -d "$SHELL_DATA" ]] && roots+=( ".local/share/$(basename "$SHELL_DATA")" )
  [[ -d "$SHELL_CFG" ]]  && roots+=( ".config/$(basename "$SHELL_CFG")" )
  [[ ${#roots[@]} -eq 0 ]] && die "no HYVE state found under \$HOME — is this a HYVE Ether OS box?"

  local ex=( "${EXCLUDES[@]}" ); [[ $lean -eq 1 ]] && ex+=( "${LEAN_EXCLUDES[@]}" )
  local comp=gzip ext=gz
  command -v zstd >/dev/null 2>&1 && { comp=zstd; ext=zst; }
  c "packing state tree (${roots[*]}) with $comp ..."
  tar -cp "${ex[@]}" --$comp -f "$work/state.tar.$ext" -C "$HOME" "${roots[@]}"

  # 2) Custom Ollama models — copy each tag's manifest + every blob it references.
  local md; md="$(ollama_models_dir)"
  if [[ -n "$md" ]]; then
    for tag in "${CUSTOM_MODELS[@]}"; do
      local name="${tag%%:*}" ver="${tag##*:}"
      local man="$md/manifests/registry.ollama.ai/library/$name/$ver"
      [[ -f "$man" ]] || { c "  (skip $tag — not present)"; continue; }
      c "  capturing model $tag"
      install -D "$man" "$work/ollama/models/manifests/registry.ollama.ai/library/$name/$ver"
      grep -oE 'sha256:[a-f0-9]{64}' "$man" | sort -u | while read -r dg; do
        local blob="$md/blobs/sha256-${dg#sha256:}"
        [[ -f "$blob" ]] && install -D "$blob" "$work/ollama/models/blobs/sha256-${dg#sha256:}"
      done
    done
  else
    c "  (no ollama models dir found — grown models NOT captured; verify manually)"
  fi

  # 3) Manifest + integrity.
  {
    echo "HYVE Carry-Over capsule"
    echo "tool_version=$VERSION"
    echo "created_utc=$stamp"
    echo "source_host=$(hostname)"
    echo "roots=${roots[*]}"
    echo "lean=$lean"
    echo "custom_models=${CUSTOM_MODELS[*]}"
  } > "$work/MANIFEST.txt"
  ( cd "$work" && find . -type f ! -name SHA256SUMS -exec sha256sum {} + > SHA256SUMS )

  c "sealing capsule ..."
  tar -cf "$out" -C "$work" .
  rm -rf "$work"

  if [[ $encrypt -eq 1 ]]; then
    command -v openssl >/dev/null 2>&1 || die "--encrypt needs openssl"
    c "encrypting (AES-256; you will be prompted for a passphrase) ..."
    openssl enc -aes-256-cbc -pbkdf2 -salt -in "$out" -out "$out.enc"
    shred -u "$out" 2>/dev/null || rm -f "$out"
    out="$out.enc"
  fi

  local sz; sz="$(du -h "$out" | cut -f1)"
  c "DONE → $out  ($sz)"
  echo
  echo "  ⚠  This capsule contains PRIVATE KEYS (messaging identity, vault key,"
  echo "     device-pair tokens, bus seed). Move it over USB, or use --encrypt"
  echo "     before sending it over any network. Delete it after import."
  echo
  echo "  Next:  copy it to the new machine and run:"
  echo "         hyve-carryover import $(basename "$out")"
}

# --------------------------------------------------------------------------
import_capsule(){
  local cap="${1:-}"; [[ -f "$cap" ]] || die "usage: hyve-carryover import <capsule>"
  cap="$(readlink -f "$cap")"
  local work; work="$(mktemp -d)"

  if [[ "$cap" == *.enc ]]; then
    command -v openssl >/dev/null 2>&1 || die "encrypted capsule needs openssl"
    c "decrypting (enter the passphrase used at export) ..."
    openssl enc -d -aes-256-cbc -pbkdf2 -in "$cap" -out "$work/capsule.tar"
    cap="$work/capsule.tar"
  fi

  c "unpacking capsule ..."
  local stg="$work/stg"; mkdir -p "$stg"
  tar -xf "$cap" -C "$stg"
  [[ -f "$stg/MANIFEST.txt" ]] || die "not a HYVE Carry-Over capsule (no MANIFEST.txt)"
  ( cd "$stg" && sha256sum -c SHA256SUMS --quiet ) || die "integrity check FAILED — capsule corrupt"
  c "manifest:"; sed 's/^/    /' "$stg/MANIFEST.txt"

  echo
  read -r -p "  Stop HYVE + ollama and restore over this machine's state? [y/N] " ok
  [[ "$ok" == "y" || "$ok" == "Y" ]] || die "aborted by user"

  # Quiesce so nothing overwrites restored state on shutdown.
  c "stopping services (best effort) ..."
  systemctl --user stop 'hyve-*' 2>/dev/null || true
  pkill -f 'ether-shell-ui' 2>/dev/null || true
  sudo systemctl stop ollama 2>/dev/null || pkill -f 'ollama serve' 2>/dev/null || true
  sleep 2

  # 1) Restore the state tree (-p keeps 0600/0400 perms; overwrites in place).
  c "restoring state tree into \$HOME ..."
  local st; st="$(ls "$stg"/state.tar.* 2>/dev/null | head -1)"
  [[ -f "$st" ]] || die "capsule missing state tarball"
  tar -xpf "$st" -C "$HOME"

  # WebKitCache MUST be wiped after restore (serves stale UI) — standing rule.
  rm -rf "$SHELL_DATA/WebKitCache" "$SHELL_DATA/CacheStorage" "$SHELL_DATA/GPUCache" 2>/dev/null || true
  # Force hwtier to re-detect THIS machine's hardware/tier.
  rm -f "$HYVE/hardware.json" "$HYVE/models.env" 2>/dev/null || true

  # 2) Restore custom Ollama models into this box's models dir.
  local md; md="$(ollama_models_dir)"
  if [[ -d "$stg/ollama/models" && -n "$md" ]]; then
    c "restoring grown models into $md (may need sudo for a system ollama) ..."
    if [[ -w "$md" ]]; then cp -a "$stg/ollama/models/." "$md/";
    else sudo cp -a "$stg/ollama/models/." "$md/" && sudo chown -R "$(stat -c '%U:%G' "$md")" "$md"; fi
  fi

  # 3) Bring services back + re-tier.
  c "restarting services ..."
  sudo systemctl start ollama 2>/dev/null || (nohup ollama serve >/dev/null 2>&1 &) || true
  command -v hyve-hwtier >/dev/null 2>&1 && hyve-hwtier || true
  systemctl --user start 'hyve-*' 2>/dev/null || true

  rm -rf "$work"
  echo
  c "RESTORE COMPLETE. Verify:"
  echo "    • Activation:  cat ~/.hyve/activation.json   (or re-enter your key at the gate)"
  echo "    • Omega brain: curl -s localhost:7895/health ; ls -la ~/.hyve/omega/mind/self_model.json"
  echo "    • Models:      ollama list   (expect ${CUSTOM_MODELS[*]})"
  echo "    • Ledger:      the SHA-256 chain in ~/.hyve/audit/os-ledger.jsonl re-verifies as-is (keyless)"
  echo "    • Reboot once so the Depot activation service starts cleanly."
}

# --------------------------------------------------------------------------
wizard(){
  echo
  printf '\033[1;33m  H Y V E   C A R R Y - O V E R\033[0m   v%s\n' "$VERSION"
  echo "  Move your HYVE Ether OS — memory, learned behavior, grown models,"
  echo "  identity — to new hardware, losing nothing."
  echo
  echo "    1) Export this machine to a capsule   (I'm moving OFF this device)"
  echo "    2) Import a capsule into this machine  (this is the NEW device)"
  echo "    3) Help"
  echo "    q) Quit"
  echo
  read -r -p "  Choose: " ch
  case "$ch" in
    1) local enc="" ln=""
       read -r -p "  Encrypt the capsule? (recommended if it crosses a network) [y/N] " e
       [[ "$e" == y || "$e" == Y ]] && enc="--encrypt"
       read -r -p "  Lean capsule? (skip large media output cinema/spark/pulse) [y/N] " l
       [[ "$l" == y || "$l" == Y ]] && ln="--lean"
       export_capsule $ln $enc ;;
    2) read -r -p "  Path to the .hyvecapsule file: " p; import_capsule "$p" ;;
    3) usage ;;
    q|Q) exit 0 ;;
    *) die "unknown choice" ;;
  esac
}

usage(){ cat <<EOF
hyve-carryover v$VERSION — move a HYVE Ether OS install to new hardware, losing nothing.

  On the OLD machine:   hyve-carryover export [--lean] [--encrypt]
      --lean      keep the OS brain + identity but drop large media output
                  (cinema/spark/pulse) for a smaller capsule
      --encrypt   AES-256 encrypt the capsule (do this if it crosses a network)

  On the NEW machine:   hyve-carryover import <capsule.hyvecapsule[.enc]>

  No arguments launches an interactive wizard.

What travels:  ~/.hyve (Omega mind+memory, skills, ledger, identity keys,
               activation, all organ state), ~/hyve-ascent (evolution spine),
               the shell profile, and the locally-built custom Ollama models
               (omega/apprentice) — minus logs, caches and telemetry.
What doesn't:  the license is offline + not HW-bound, so you can simply re-enter
               your product key on the new box instead of copying activation.json.
EOF
}

case "${1:-}" in
  export) shift; export_capsule "$@" ;;
  import) shift; import_capsule "$@" ;;
  -v|--version) echo "hyve-carryover $VERSION" ;;
  -h|--help|help) usage ;;
  "") if [[ -t 0 ]]; then wizard; else usage; fi ;;
  *) die "unknown command '$1' (try: hyve-carryover --help)" ;;
esac
