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

# User-only installer for Qubes AppVM persistence model.
# Nothing is written to /usr, /etc, or other root-owned paths.
#
# Installs into $HOME:
#   - nsigner -> ~/.local/bin/nsigner
#   - startup helper -> ~/start_nsigner.sh
#
# Usage:
#   bash install_qube_fips_nsigner.sh
#   bash install_qube_fips_nsigner.sh --help
#
# Optional env vars:
#   NSIGNER_VERSION=vX.Y.Z         # optional override; default is latest release tag
#   NSIGNER_GITEA_TOKEN=<token>    # if n_signer release assets are private
#   NSIGNER_BINARY_URL=<direct url to nsigner_static_x86_64>

NSIGNER_VERSION="${NSIGNER_VERSION:-}"

PREFIX_BIN="${HOME}/.local/bin"

log() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; }
warn() { printf "\033[1;33m[WARN]\033[0m %s\n" "$*"; }
err() { printf "\033[1;31m[ERR ]\033[0m %s\n" "$*"; }

show_help() {
  cat <<EOF
Usage: bash install_qube_fips_nsigner.sh [options]

User-only install (Qubes AppVM friendly):
  - n_signer ${NSIGNER_VERSION}
  - signer startup helper script

Options:
  -h, --help      Show this help and exit

Optional env vars:
  NSIGNER_VERSION=vX.Y.Z         # optional override; default is latest release tag
  NSIGNER_GITEA_TOKEN=<token>    # required if n_signer release assets are private
  NSIGNER_BINARY_URL=<direct url to nsigner_static_x86_64>

Install paths:
  ~/.local/bin/nsigner
  ~/start_nsigner.sh
EOF
}

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || {
    err "Missing command: $1"
    exit 1
  }
}

install_runtime_deps() {
  if command -v apt-get >/dev/null 2>&1; then
    log "Installing runtime dependencies via apt"
    sudo apt-get update
    sudo apt-get install -y ca-certificates curl jq
  elif command -v dnf >/dev/null 2>&1; then
    log "Installing runtime dependencies via dnf"
    sudo dnf install -y ca-certificates curl jq
  else
    err "Unsupported distro: need apt-get or dnf to install runtime dependencies"
    exit 1
  fi
}

prepare_dirs() {
  mkdir -p "${PREFIX_BIN}"
}

resolve_nsigner_version() {
  local headers=()
  local latest_tag=""

  if [[ -n "${NSIGNER_VERSION}" ]]; then
    return 0
  fi

  if [[ -n "${NSIGNER_GITEA_TOKEN:-}" ]]; then
    headers=(-H "Authorization: token ${NSIGNER_GITEA_TOKEN}")
  fi

  latest_tag="$(curl -fsSL "${headers[@]}" "https://git.laantungir.net/api/v1/repos/laantungir/n_signer/releases" \
    | jq -r '.[0].tag_name // empty' || true)"

  if [[ -z "${latest_tag}" ]]; then
    err "Could not resolve latest n_signer release tag from API."
    err "Set NSIGNER_VERSION explicitly (e.g. NSIGNER_VERSION=v0.0.11)."
    exit 1
  fi

  NSIGNER_VERSION="${latest_tag}"
}

download_nsigner_asset_url() {
  local headers=()
  local api_tag_url="https://git.laantungir.net/api/v1/repos/laantungir/n_signer/releases/tags/${NSIGNER_VERSION}"

  if [[ -n "${NSIGNER_GITEA_TOKEN:-}" ]]; then
    headers=(-H "Authorization: token ${NSIGNER_GITEA_TOKEN}")
  fi

  curl -fsSL "${headers[@]}" "${api_tag_url}" \
    | jq -r '.assets[]?.browser_download_url // empty' \
    | grep -E 'nsigner_static_x86_64$' \
    | head -n1 || true
}

verify_installed_version() {
  local expected="$1"
  local got_line=""
  local got_ver=""

  if [[ ! -x "${PREFIX_BIN}/nsigner" ]]; then
    err "Installed binary missing: ${PREFIX_BIN}/nsigner"
    exit 1
  fi

  got_line="$("${PREFIX_BIN}/nsigner" --version 2>/dev/null || true)"
  got_ver="$(printf '%s\n' "${got_line}" | awk '{print $2}')"

  if [[ -z "${got_ver}" ]]; then
    err "Could not determine installed n_signer version from: ${got_line}"
    exit 1
  fi

  if [[ "${got_ver}" != "${expected}" ]]; then
    err "Downloaded binary version mismatch: expected ${expected}, got ${got_ver}"
    err "Release asset appears stale or mislabeled."
    err "Use NSIGNER_BINARY_URL to pin a known-good binary, or wait for a rebuilt release artifact."
    exit 1
  fi
}

install_nsigner() {
  local release_page=""

  resolve_nsigner_version
  release_page="https://git.laantungir.net/laantungir/n_signer/releases/tag/${NSIGNER_VERSION}"

  log "Installing n_signer ${NSIGNER_VERSION}"
  log "Release page: ${release_page}"

  local asset_url="${NSIGNER_BINARY_URL:-}"
  if [[ -z "${asset_url}" ]]; then
    asset_url="$(download_nsigner_asset_url)"
  fi

  if [[ -z "${asset_url}" ]]; then
    err "Could not find downloadable n_signer x86_64 release binary for ${NSIGNER_VERSION}."
    err "Provide NSIGNER_BINARY_URL or NSIGNER_GITEA_TOKEN so the release asset can be resolved."
    exit 1
  fi

  log "Using n_signer binary URL: ${asset_url}"
  if [[ -n "${NSIGNER_GITEA_TOKEN:-}" ]]; then
    curl -fL -H "Authorization: token ${NSIGNER_GITEA_TOKEN}" -o "${PREFIX_BIN}/nsigner" "${asset_url}"
  else
    curl -fL -o "${PREFIX_BIN}/nsigner" "${asset_url}"
  fi
  chmod 0755 "${PREFIX_BIN}/nsigner"

  verify_installed_version "${NSIGNER_VERSION}"
  log "Installed ${PREFIX_BIN}/nsigner from release binary"
}

write_signer_start_script() {
  local script_path="${HOME}/start_nsigner.sh"

  cat >"${script_path}" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

export PATH="$HOME/.local/bin:$PATH"
LISTEN_TARGET="${NSIGNER_LISTEN_TARGET:-tcp:[::]:8080}"

# If first arg is "qrexec", start in unix bridge mode for Qubes qrexec.
# Otherwise, pass any extra args through to nsigner (e.g. --allow-all).
if [[ "${1:-}" == "qrexec" ]]; then
  LISTEN_TARGET="unix"
  EXTRA_ARGS="--socket-name nsigner --bridge-source-trusted"
  shift
else
  EXTRA_ARGS="$*"
fi

echo "=== n_signer startup ==="
echo "listen target: ${LISTEN_TARGET}"

# Optional: print current FIPS identity info if fipsctl is available.
if command -v fipsctl >/dev/null 2>&1; then
  if fipsctl show status >/dev/null 2>&1; then
    STATUS_JSON="$(fipsctl show status)"
  elif sudo -n fipsctl show status >/dev/null 2>&1; then
    STATUS_JSON="$(sudo -n fipsctl show status)"
  else
    STATUS_JSON=""
  fi

  if [[ -n "${STATUS_JSON}" ]]; then
    FIPS_IPV6="$(printf '%s\n' "${STATUS_JSON}" | sed -n 's/.*"ipv6_addr": "\([^"]*\)".*/\1/p')"
    FIPS_NPUB="$(printf '%s\n' "${STATUS_JSON}" | sed -n 's/.*"npub": "\([^"]*\)".*/\1/p')"
    LISTEN_PORT="$(printf '%s\n' "${LISTEN_TARGET}" | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')"
    [[ -n "${FIPS_IPV6}" ]] && echo "fips ipv6: ${FIPS_IPV6}"
    [[ -n "${FIPS_NPUB}" ]] && echo "fips npub: ${FIPS_NPUB}"
    if [[ -n "${FIPS_NPUB}" && -n "${LISTEN_PORT}" ]]; then
      echo "fips address: http://${FIPS_NPUB}.fips:${LISTEN_PORT}"
    fi
  else
    echo "fips status: unavailable (run as user in fips group or with sudo)"
  fi
fi

echo
echo "Starting signer..."
echo "On first remote request, approve in prompt with [y] or [a]."
if [[ -n "${EXTRA_ARGS}" ]]; then
  exec "$HOME/.local/bin/nsigner" --listen "${LISTEN_TARGET}" ${EXTRA_ARGS}
else
  exec "$HOME/.local/bin/nsigner" --listen "${LISTEN_TARGET}"
fi
EOF

  chmod 0755 "${script_path}"
  log "Wrote ${script_path}"
}

post_checks() {
  export PATH="${PREFIX_BIN}:${PATH}"

  log "Running post-install checks"
  require_cmd nsigner
  nsigner --version || true

  log "User binaries installed in: ${PREFIX_BIN}"
  log "If needed, add to shell PATH: export PATH=\"${PREFIX_BIN}:\$PATH\""
}

main() {
  if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
    show_help
    exit 0
  fi

  if [[ $# -gt 0 ]]; then
    err "Unknown option: $1"
    show_help
    exit 1
  fi

  install_runtime_deps
  prepare_dirs
  install_nsigner
  write_signer_start_script
  post_checks

  log "Completed user-only install of n_signer"
  log "Start signer with: ~/start_nsigner.sh"
}

main "$@"
