#!/usr/bin/env bash
# shellcheck disable=SC2034
# SPDX-License-Identifier: MPL-2.0
# Reliably detect and switch between SSH agents like keychain, yubikey-agent, 1Password, etc.

if [[ $DEBUG != "" ]]; then
  set -x
fi

logOps() {
  PREFIX=$3

  if [[ $PREFIX != "" ]]; then
    LOGOPS_PREFIX="[ssh-agent-loader::$PREFIX]"
  else
    LOGOPS_PREFIX="[ssh-agent-loader]"
  fi

  if [[ $1 == "debug" ]]; then
    [[ $DEBUG != "" ]] && echo "[ssh-agent-loader] debug: $2"
  elif [[ $1 == "warn" ]]; then
    echo "$LOGOPS_PREFIX warning: $2"
  elif [[ $1 == "error" ]]; then
    echo "$LOGOPS_PREFIX error: $2"
  else
    [[ $SSH_AGENT_LOADER_SLIENT != "1" ]] && echo "$LOGOPS_PREFIX $2"
  fi
}

# Workaround in cases where XDG_RUNTIME_DIR is undefined on login
if [[ -z "${XDG_RUNTIME_DIR}" ]]; then
  logOps warn "XDG_RUNTIME_DIR is possibly undefined, see https://github.com/swaywm/sway/issues/7202"
  logOps warn "for context and https://wiki.archlinux.org/title/XDG_Base_Directory for docs"
  logOps warn "Temporarily using '/run/user/$(id -u)' for XDG_RUNTIME_DIR in 3s..."
  sleep 3
  XDG_RUNTIME_DIR="/run/user/$(id -u)"
  export XDG_RUNTIME_DIR
fi

# do feature detection if keychain is installed
if command -v keychain >> /dev/null; then
  FF_KEYCHAIN=1
else
  FF_KEYCHAIN=0
fi

try_keychain_ssh_agent() {
  if [[ $FF_KEYCHAIN == "1" ]]; then
    logOps info "attempting to use keychain for SSH agents" keychain
    if [[ "$SSH_AGENT_LOADER_SLIENT" == "" ]]; then
      eval "$(keychain --eval --ssh-spawn-gpg --ssh-allow-gpg)"
    else
      eval "$(keychain --eval --ssh-spawn-gpg --ssh-allow-gpg --quiet)"
    fi
  else
    logOps warn "keychain is not in PATH yet" keychain
    return 1
  fi
}

# Ripped off NixOS-generated set-environment on my laptop for yubikey-agent setup
try_yubikey_agent() {
  YUBIKEY_AGENT_AUTH_SOCK="${XDG_RUNTIME_DIR}/yubikey-agent/yubikey-agent.sock"
  if [[ -f "${YUBIKEY_AGENT_AUTH_SOCK}" ]]; then
    logOps info "attempting to use Yubikey SSH agent" yubikey-agent
    if ! SSH_AUTH_SOCK=${YUBIKEY_AGENT_AUTH_SOCK} ssh-add -l >> /dev/null 2>&1; then
      logOps warn "something went wrong while checking for Yubikey SSH agent availability" yubikey-agent
      logOps warn "is your Yubikey plugged in properly?" yubikey-agent
      return 1
    fi
  else
    logOps error "Yubikey SSH agent seems to be not available on this host" yubikey-agent
    return 1
  fi
}

try_1password_ssh_agent() {
  OP_SSH_AUTH_SOCK="$HOME/.1password/agent.sock"
  if [[ ! -S "$OP_SSH_AUTH_SOCK" ]]; then
    logOps warn "1Password SSH agent isn't enabled or desktop app isn't installed yet" 1password
    return 1
  fi

  logOps info "attempting to use 1Password SSH agent" 1password
  if ! SSH_AUTH_SOCK=$OP_SSH_AUTH_SOCK ssh-add -l >> /dev/null 2>&1; then
    logOps warn "something went wrong while checking for 1Password SSH agent availability" 1password
    logOps warn "unlock the desktop app first or enable SSH agent from settings" 1password
    return 1
  fi
  export SSH_AUTH_SOCK=$OP_SSH_AUTH_SOCK FF_USE_OP_CLI_PLUGINS=true
  unset OP_SSH_AUTH_SOCK
}

ssh-agent-loader() {
  if [[ $1 == "" || $1 == "auto" ]]; then
    if [[ $SSH_CONNECTION != "" ]] && [[ $VSCODE_IPC_HOOK_CLI != "" ]]; then
      logOps info "automatic detection is disabled while you're in a VS Code Remote SSH/Tunnels session"
      logOps info "to enable SSH agent, please manually invoke the shell function with desired agent"
      return
    fi

    export OLD_SSH_AUTH_SOCK="$SSH_AUTH_SOCK"

    unset SSH_AGENT_PID SSH_AUTH_SOCK
    if try_1password_ssh_agent; then
      return
    elif try_keychain_ssh_agent; then
      return
    elif try_yubikey_agent; then
      return
    else
      logOps error "SSH agent seems to be failed to load at the moment"
      logOps error "try again later by manually invoking the shell function"
      return 1
    fi
  elif [[ $1 == "1passowrd" || $1 == "op" ]]; then
    unset SSH_AGENT_PID SSH_AUTH_SOCK
    try_1password_ssh_agent
  elif [[ $1 == "keychain" ]]; then
    try_keychain_ssh_agent
  elif [[ $1 == "yubikey" ]]; then
    try_yubikey_agent
  else
    echo "Usage: ssh-agent-loader [auto|[1password|op|1p]|keychain|yubikey]"
    return 1
  fi
}

# Avoid source loops on subsequent file resourcing.
if [[ -z "$SSH_AGENT_LOADED" ]]; then
  # automatically detect things as we source this
  [[ "$FF_SKIP_AUTO_SSH_AGENT_LOADER" == "" ]] && ssh-agent-loader auto
  export SSH_AGENT_LOADED=1
fi

if [[ $DEBUG == "1" ]]; then
  set +x
fi
