#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
Usage:
  portable.sh build   [OUT_DIR] [NODE_TARBALL_URL]
  portable.sh pack    [OUT_DIR] [ARCHIVE]
  portable.sh unpack  [ARCHIVE] [OUT_DIR]

Defaults:
  OUT_DIR: codex-portable
  ARCHIVE: codex-portable.tgz
  NODE_VERSION: 20.19.6
  NODE_TARBALL_URL: auto (official if glibc >= 2.28, otherwise unofficial glibc-217)
  Archive password: optional; if set, archive is encrypted with openssl (AES-256-CBC)
  Progress: if pv is installed, pack/unpack shows real-time progress
EOF
}

prompt() {
  local label="$1"
  local def="${2:-}"
  local value=""
  if [[ -n "$def" ]]; then
    if [[ -t 0 ]]; then
      read -r -p "$label [$def]: " value
    else
      read -r -p "$label [$def]: " value < /dev/tty
    fi
    echo "${value:-$def}"
  else
    if [[ -t 0 ]]; then
      read -r -p "$label: " value
    else
      read -r -p "$label: " value < /dev/tty
    fi
    echo "$value"
  fi
}

choose_action() {
  if [[ -t 1 ]]; then
    echo "Select action:"
    echo "1) build"
    echo "2) pack"
    echo "3) unpack"
  else
    printf "%s\n" "Select action:" "1) build" "2) pack" "3) unpack" > /dev/tty
  fi
  local choice
  choice="$(prompt "Choice" "1")"
  case "$choice" in
    1|build) echo "build" ;;
    2|pack) echo "pack" ;;
    3|unpack) echo "unpack" ;;
    *) echo "" ;;
  esac
}

cmd="${1:-}"
if [[ -z "$cmd" ]]; then
  cmd="$(choose_action)"
  if [[ -z "$cmd" ]]; then
    usage
    exit 1
  fi
fi
case "$cmd" in
  build)
    OUT_DIR="${2:-}"
    NODE_TARBALL_URL="${3:-}"
    NODE_VERSION="${NODE_VERSION:-20.19.6}"
    if [[ -z "$OUT_DIR" ]]; then
      OUT_DIR="$(prompt "OUT_DIR" "codex-portable")"
    fi
    if [[ -z "$NODE_TARBALL_URL" ]]; then
      mode="$(prompt "Node source (auto/official/unofficial/url)" "auto")"
      case "$mode" in
        official)
          NODE_TARBALL_URL="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
          ;;
        unofficial)
          NODE_TARBALL_URL="https://unofficial-builds.nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64-glibc-217.tar.xz"
          ;;
        url)
          NODE_TARBALL_URL="$(prompt "NODE_TARBALL_URL")"
          ;;
        *)
          NODE_TARBALL_URL=""
          ;;
      esac
    fi
    PASSWORD=""
    if [[ -t 0 ]]; then
      read -r -s -p "Password: " PASSWORD
      echo
    else
      read -r -s -p "Password: " PASSWORD < /dev/tty
      echo > /dev/tty
    fi
    INSTALL_URL="${AUTH_INSTALL_URL:-https://codex.naskel.petrsu.ru/codex-install}"
    if ! command -v python3 >/dev/null 2>&1; then
      echo "Error: python3 is required to build install request." >&2
      exit 1
    fi
    payload="$(python3 - "$PASSWORD" <<'PY'
import json,sys
print(json.dumps({"password": sys.argv[1] if len(sys.argv) > 1 else ""}))
PY
)"
    if command -v curl >/dev/null 2>&1; then
      install_response="$(curl -fsS -X POST -H 'Content-Type: application/json' -d "$payload" "$INSTALL_URL")"
    elif command -v wget >/dev/null 2>&1; then
      install_response="$(wget -qO- --header='Content-Type: application/json' --post-data="$payload" "$INSTALL_URL")"
    else
      echo "Error: need curl or wget to contact auth server." >&2
      exit 1
    fi
    INSTALL_TOKEN="$(python3 - "$install_response" <<'PY'
import json,sys
try:
    data=json.loads(sys.argv[1])
    print(data.get("install_token", ""))
except Exception:
    print("")
PY
)"
    if [[ -z "$INSTALL_TOKEN" ]]; then
      echo "Error: auth server response missing install_token." >&2
      exit 1
    fi
    TMP_DIR="${TMP_DIR:-/tmp/codex-portable-build}"

    rm -rf "$OUT_DIR" "$TMP_DIR"
    mkdir -p "$OUT_DIR" "$TMP_DIR"

    if [[ -z "$NODE_TARBALL_URL" ]]; then
      glibc_ver=""
      if command -v getconf >/dev/null 2>&1; then
        glibc_ver="$(getconf GNU_LIBC_VERSION 2>/dev/null | awk '{print $2}')"
      elif command -v ldd >/dev/null 2>&1; then
        glibc_ver="$(ldd --version 2>/dev/null | head -n1 | awk '{print $NF}')"
      fi

      if [[ -n "$glibc_ver" ]] && [[ "$(printf '%s\n' "2.28" "$glibc_ver" | sort -V | head -n1)" == "2.28" ]]; then
        remote_url="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
      else
        remote_url="https://unofficial-builds.nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64-glibc-217.tar.xz"
      fi

      mirror_base="${NODE_MIRROR_BASE:-https://codex.naskel.petrsu.ru/node}"
      mirror_url="${mirror_base}/${remote_url##*/}"
      if command -v curl >/dev/null 2>&1; then
        if curl -fsI "$mirror_url" >/dev/null 2>&1; then
          NODE_TARBALL_URL="$mirror_url"
        else
          NODE_TARBALL_URL="$remote_url"
        fi
      elif command -v wget >/dev/null 2>&1; then
        if wget -q --spider "$mirror_url"; then
          NODE_TARBALL_URL="$mirror_url"
        else
          NODE_TARBALL_URL="$remote_url"
        fi
      else
        NODE_TARBALL_URL="$remote_url"
      fi
    fi

    echo "Downloading Node from: $NODE_TARBALL_URL"
    if command -v curl >/dev/null 2>&1; then
      curl -fL "$NODE_TARBALL_URL" -o "$TMP_DIR/node.tar.xz"
    elif command -v wget >/dev/null 2>&1; then
      wget -O "$TMP_DIR/node.tar.xz" "$NODE_TARBALL_URL"
    else
      echo "Error: need curl or wget." >&2
      exit 1
    fi

    tar -xJf "$TMP_DIR/node.tar.xz" -C "$TMP_DIR"
    node_dir="$(find "$TMP_DIR" -maxdepth 1 -type d -name 'node-v*' | head -n1)"
    if [[ -z "$node_dir" ]]; then
      echo "Error: failed to unpack Node tarball." >&2
      exit 1
    fi

    mv "$node_dir" "$OUT_DIR/node"

    echo "Installing Codex..."
    export PATH="$OUT_DIR/node/bin:$PATH"
    "$OUT_DIR/node/bin/npm" install -g --prefix "$OUT_DIR/codex" @openai/codex

    cat > "$OUT_DIR/run.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AUTH_SERVER_URL="${AUTH_SERVER_URL:-https://codex.naskel.petrsu.ru/codex-auth}"
if [[ ! -d /dev/shm ]]; then
  echo "Error: /dev/shm not available. Refusing to store credentials on disk." >&2
  exit 1
fi
umask 077
CODEX_HOME="$ROOT_DIR/.codex"
RAM_DIR="$(mktemp -d /dev/shm/codex-auth.XXXXXX)"
mkdir -p "$CODEX_HOME/sessions"
cleanup() {
  rm -rf "$RAM_DIR"
  rm -f "$CODEX_HOME/auth.json"
}
trap cleanup EXIT

request_auth() {
  local token="$1"
  local payload
  payload="$("$ROOT_DIR/node/bin/node" -e 'const t=process.argv[1]||"";process.stdout.write(JSON.stringify({token:t}));' "$token")"
  if command -v curl >/dev/null 2>&1; then
    curl -fsS -X POST -H 'Content-Type: application/json' -d "$payload" "$AUTH_SERVER_URL"
  elif command -v wget >/dev/null 2>&1; then
    wget -qO- --header='Content-Type: application/json' --post-data="$payload" "$AUTH_SERVER_URL"
  else
    echo "Error: need curl or wget to contact auth server." >&2
    exit 1
  fi
}

TOKEN_PATH="$CODEX_HOME/install.token"
if [[ ! -f "$TOKEN_PATH" ]]; then
  echo "Error: install token missing. Rebuild with codexport.sh build." >&2
  exit 1
fi
TOKEN="$(tr -d '\r\n' < "$TOKEN_PATH")"
if [[ -z "$TOKEN" ]]; then
  echo "Error: install token empty. Rebuild with codexport.sh build." >&2
  exit 1
fi

AUTH_RESPONSE="$(request_auth "$TOKEN")"

AUTH_RESPONSE="$AUTH_RESPONSE" ROOT_DIR="$ROOT_DIR" HOME_DIR="$RAM_DIR" \
  "$ROOT_DIR/node/bin/node" -e '
const fs = require("fs");
const path = require("path");
const data = JSON.parse(process.env.AUTH_RESPONSE || "{}");
if (!data.auth_json) {
  console.error("Error: auth server response missing auth_json.");
  process.exit(1);
}
const homeDir = process.env.HOME_DIR;
fs.mkdirSync(homeDir, { recursive: true });
const authPath = path.join(homeDir, "auth.json");
const proxyPath = path.join(homeDir, "proxy.env");
const authPayload = typeof data.auth_json === "string"
  ? data.auth_json
  : JSON.stringify(data.auth_json, null, 2);
fs.writeFileSync(authPath, authPayload + (authPayload.endsWith("\n") ? "" : "\n"));
const lines = [];
if (data.proxy_url) lines.push(`PROXY_URL="${data.proxy_url}"`);
if (data.no_proxy) lines.push(`NO_PROXY="${data.no_proxy}"`);
fs.writeFileSync(proxyPath, lines.join("\n") + (lines.length ? "\n" : ""));
'

ln -sf "$RAM_DIR/auth.json" "$CODEX_HOME/auth.json"
export HOME="$ROOT_DIR"
export CODEX_HOME="$CODEX_HOME"
if [[ -f "$RAM_DIR/proxy.env" ]]; then
  # Expected variables: PROXY_URL, NO_PROXY
  . "$RAM_DIR/proxy.env"
fi
if [[ -n "${PROXY_URL:-}" ]]; then
  export HTTP_PROXY="$PROXY_URL"
  export HTTPS_PROXY="$PROXY_URL"
  export ALL_PROXY="$PROXY_URL"
fi
if [[ -n "${NO_PROXY:-}" ]]; then
  export NO_PROXY="$NO_PROXY"
fi
export PATH="$ROOT_DIR/node/bin:$ROOT_DIR/codex/bin:$PATH"
exec "$ROOT_DIR/node/bin/node" \
  "$ROOT_DIR/codex/lib/node_modules/@openai/codex/bin/codex.js" \
  "$@"
EOF

    CODEX_HOME="$OUT_DIR/.codex"
    mkdir -p "$CODEX_HOME"
    printf '%s\n' "$INSTALL_TOKEN" > "$CODEX_HOME/install.token"
    chmod 600 "$CODEX_HOME/install.token"

    chmod +x "$OUT_DIR/run.sh"
    echo "Portable build ready: $OUT_DIR"
    echo "Run: $OUT_DIR/run.sh"
    ;;

  pack)
    OUT_DIR="${2:-}"
    ARCHIVE="${3:-}"
    if [[ -z "$OUT_DIR" ]]; then
      OUT_DIR="$(prompt "OUT_DIR" "codex-portable")"
    fi
    if [[ -z "$ARCHIVE" ]]; then
      ARCHIVE="$(prompt "ARCHIVE" "codex-portable.tgz")"
    fi
    if [[ ! -d "$OUT_DIR" ]]; then
      echo "Error: $OUT_DIR not found." >&2
      exit 1
    fi
    ARCHIVE_PASSWORD=""
    if [[ -t 0 ]]; then
      read -r -s -p "Archive password (leave empty for none): " ARCHIVE_PASSWORD
      echo
    else
      read -r -s -p "Archive password (leave empty for none): " ARCHIVE_PASSWORD < /dev/tty
      echo > /dev/tty
    fi
    if [[ -n "$ARCHIVE_PASSWORD" ]]; then
      if ! command -v openssl >/dev/null 2>&1; then
        echo "Error: openssl is required for encrypted archives." >&2
        exit 1
      fi
      if command -v pv >/dev/null 2>&1; then
        tar -czf - -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")" \
          | pv \
          | openssl enc -aes-256-cbc -pbkdf2 -salt \
            -pass "pass:$ARCHIVE_PASSWORD" \
            -out "$ARCHIVE"
      else
        tmp_archive="$(mktemp /tmp/codex-archive.XXXXXX.tgz)"
        tar -czf "$tmp_archive" -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")"
        openssl enc -aes-256-cbc -pbkdf2 -salt \
          -pass "pass:$ARCHIVE_PASSWORD" \
          -in "$tmp_archive" -out "$ARCHIVE"
        rm -f "$tmp_archive"
      fi
      echo "Packed (encrypted): $ARCHIVE"
    else
      if command -v pv >/dev/null 2>&1; then
        tar -czf - -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")" | pv > "$ARCHIVE"
      else
        tar -czf "$ARCHIVE" -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")"
      fi
      echo "Packed: $ARCHIVE"
    fi
    ;;

  unpack)
    ARCHIVE="${2:-}"
    OUT_DIR="${3:-}"
    if [[ -z "$ARCHIVE" ]]; then
      ARCHIVE="$(prompt "ARCHIVE" "codex-portable.tgz")"
    fi
    if [[ -z "$OUT_DIR" ]]; then
      OUT_DIR="$(prompt "OUT_DIR" "codex-portable")"
    fi
    if [[ ! -f "$ARCHIVE" ]]; then
      echo "Error: $ARCHIVE not found." >&2
      exit 1
    fi
    ARCHIVE_PASSWORD=""
    if [[ -t 0 ]]; then
      read -r -s -p "Archive password (leave empty if none): " ARCHIVE_PASSWORD
      echo
    else
      read -r -s -p "Archive password (leave empty if none): " ARCHIVE_PASSWORD < /dev/tty
      echo > /dev/tty
    fi
    rm -rf "$OUT_DIR"
    if [[ -n "$ARCHIVE_PASSWORD" ]]; then
      if ! command -v openssl >/dev/null 2>&1; then
        echo "Error: openssl is required for encrypted archives." >&2
        exit 1
      fi
      if command -v pv >/dev/null 2>&1; then
        pv "$ARCHIVE" \
          | openssl enc -d -aes-256-cbc -pbkdf2 -pass "pass:$ARCHIVE_PASSWORD" \
          | tar -xzf - -C "$(dirname "$OUT_DIR")"
      else
        tmp_archive="$(mktemp /tmp/codex-archive.XXXXXX.tgz)"
        openssl enc -d -aes-256-cbc -pbkdf2 \
          -pass "pass:$ARCHIVE_PASSWORD" \
          -in "$ARCHIVE" -out "$tmp_archive"
        tar -xzf "$tmp_archive" -C "$(dirname "$OUT_DIR")"
        rm -f "$tmp_archive"
      fi
    else
      if command -v pv >/dev/null 2>&1; then
        pv "$ARCHIVE" | tar -xzf - -C "$(dirname "$OUT_DIR")"
      else
        tar -xzf "$ARCHIVE" -C "$(dirname "$OUT_DIR")"
      fi
    fi
    echo "Unpacked to: $OUT_DIR"
    ;;

  *)
    usage
    exit 1
    ;;
esac
