#!/usr/bin/env bash
# secrets.sh — o cofre de segredos, do lado de cá.
#
# Toda chamada vira `sudo -n /usr/local/sbin/sshepherd-secrets <subcomando>` no
# servidor. O helper roda como root, é o único que abre o cofre, e nunca mostra
# um valor. Este script só carrega a conexão e repassa a saída, redigida.
#
# Uso:
#   secrets.sh --host <ip> --user <deploy> [--port 22] [--key <chave>] --action status
#   secrets.sh ... --action set --name NOME             (precisa de teclado: ver abaixo)
#   secrets.sh ... --action generate --name NOME [--bytes 32] --confirmed
#   secrets.sh ... --action unset --name NOME --confirmed
#   secrets.sh ... --action apply [--health http://127.0.0.1/] --confirmed   (padrão: 127.0.0.1)
#   secrets.sh ... --action rollback --confirmed
#
# `set` é a única ação que carrega um valor, e o valor vai do teclado da pessoa
# para o servidor sem passar por mais ninguém. Com terminal, este script abre a
# sessão interativa ali mesmo. Sem terminal (é o caso do agente), ele imprime o
# comando exato para a pessoa rodar e sai com 10. Não existe `--value`, e não
# existe `get`: valor não volta do servidor em texto puro.
#
# Códigos: 0 ok | 2 argumento | 3 cofre ausente | 8 serviço não subiu
#          10 sem confirmação ou sem teclado | 13 conexão | 14 helper ausente

set -uo pipefail

. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/redact.sh"

HOST=""; USER_NAME="deploy"; PORT=22; KEY_PATH=""
ACTION=""; NAME=""; BYTES=""; HEALTH=""; CONFIRMED=0
HELPER="/usr/local/sbin/sshepherd-secrets"

while [ $# -gt 0 ]; do
  case "$1" in
    --host) HOST="$2"; shift 2 ;;
    --user) USER_NAME="$2"; shift 2 ;;
    --port) PORT="$2"; shift 2 ;;
    --key) KEY_PATH="$2"; shift 2 ;;
    --action) ACTION="$2"; shift 2 ;;
    --name) NAME="$2"; shift 2 ;;
    --bytes) BYTES="$2"; shift 2 ;;
    --health) HEALTH="$2"; shift 2 ;;
    --confirmed) CONFIRMED=1; shift ;;
    *) echo "argumento desconhecido: $1" >&2; exit 2 ;;
  esac
done

[ -n "$HOST" ] || { echo "ERRO: --host é obrigatório" >&2; exit 2; }
[ -n "$ACTION" ] || { echo "ERRO: --action é obrigatório" >&2; exit 2; }

# O nome é a única coisa deste script que entra numa linha de comando remota.
# Fechado aqui E no helper: dois portões, nenhum confia no outro.
valid_name() {
  case "$1" in *[!A-Z0-9_]*|[!A-Z]*|"") return 1 ;; esac
  [ "${#1}" -le 128 ]
}
case "$ACTION" in
  set|generate|unset)
    [ -n "$NAME" ] || { echo "ERRO: --name é obrigatório para ${ACTION}" >&2; exit 2; }
    valid_name "$NAME" || { echo "ERRO: nome inválido '${NAME}': MAIUSCULAS, dígitos e sublinhado, começando por letra" >&2; exit 2; }
    ;;
  status|apply|rollback) ;;
  *) echo "ERRO: --action aceita status, set, generate, unset, apply ou rollback" >&2; exit 2 ;;
esac
case "$BYTES" in
  ""|[0-9]|[0-9][0-9]) ;;
  *) echo "ERRO: --bytes aceita só número" >&2; exit 2 ;;
esac
if [ -n "$HEALTH" ]; then
  case "$HEALTH" in
    http://127.0.0.1*|http://localhost*|https://127.0.0.1*|https://localhost*) ;;
    *) echo "ERRO: --health aceita só endereço local, por exemplo http://127.0.0.1/" >&2; exit 2 ;;
  esac
fi

SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ForwardAgent=no -o ConnectTimeout=10 -p "$PORT")
[ -n "$KEY_PATH" ] && SSH_OPTS+=(-i "${KEY_PATH/#\~/$HOME}" -o IdentitiesOnly=yes)
TARGET="${USER_NAME}@${HOST}"

# ── set: teclado da pessoa, ou o comando para ela ────────────────────────────
if [ "$ACTION" = "set" ]; then
  REMOTO="sudo -n ${HELPER} set ${NAME}"
  if [ -t 0 ] && [ -t 1 ]; then
    echo "Vou abrir uma sessão no servidor para você digitar o valor de ${NAME}."
    echo "O valor não aparece na tela e não passa por esta máquina em texto."
    echo ""
    exec ssh -t "${SSH_OPTS[@]}" "$TARGET" "$REMOTO"
  fi
  echo "ESTA AÇÃO PRECISA DE TECLADO, e esta sessão não tem terminal."
  echo ""
  echo "Rode você, no seu terminal (o valor é digitado lá, sem eco, duas vezes):"
  echo ""
  printf '  ssh -t -o ForwardAgent=no'
  [ -n "$KEY_PATH" ] && printf ' -i %q' "${KEY_PATH/#\~/$HOME}"
  printf ' -p %s %s %q\n' "$PORT" "$TARGET" "$REMOTO"
  echo ""
  echo "Depois, peça para aplicar: secrets.sh --action apply --confirmed"
  echo "SECRETS=precisa_de_teclado"
  exit 10
fi

# ── as demais: BatchMode, saída redigida ─────────────────────────────────────
case "$ACTION" in
  status)   REMOTO="sudo -n ${HELPER} status" ;;
  generate) REMOTO="sudo -n ${HELPER} generate ${NAME}${BYTES:+ --bytes ${BYTES}}" ;;
  unset)    REMOTO="sudo -n ${HELPER} unset ${NAME}" ;;
  # Sem saúde confirmada o cofre não vira "o último que funcionou". O endereço
  # padrão é o mesmo do deploy; quem tem outro passa --health.
  apply)    REMOTO="sudo -n ${HELPER} apply --health ${HEALTH:-http://127.0.0.1/}" ;;
  rollback) REMOTO="sudo -n ${HELPER} rollback" ;;
esac

if [ "$ACTION" != "status" ]; then
  echo "--------------------------------------------"
  echo " COFRE em ${HOST}"
  echo "  ação     : ${ACTION}${NAME:+ ${NAME}}"
  echo "  servidor : ${TARGET}:${PORT}"
  case "$ACTION" in
    apply|rollback) echo "  efeito   : reinicia a aplicação; se não subir, volta ao cofre anterior" ;;
    generate)       echo "  efeito   : gera o valor no servidor; ele nunca sai de lá" ;;
    unset)          echo "  efeito   : remove a variável do cofre" ;;
  esac
  echo "--------------------------------------------"
  if [ "$CONFIRMED" -ne 1 ]; then
    echo "RECUSADO: '${ACTION}' altera o servidor e exige --confirmed." >&2
    exit 10
  fi
fi

OUT=$(ssh "${SSH_OPTS[@]}" "$TARGET" "$REMOTO" 2>&1)
CODE=$?
printf '%s\n' "$OUT" | redact

if [ $CODE -eq 255 ]; then
  echo "FALHA DE CONEXAO com ${TARGET}:${PORT}" >&2
  exit 13
fi
if printf '%s' "$OUT" | grep -qE 'sudo: .*(password is required|not allowed|command not found)|No such file or directory'; then
  echo "" >&2
  echo "O cofre não está instalado neste servidor, ou '${USER_NAME}' não tem permissão para ele." >&2
  echo "Rode /sshp-provision: ele instala o helper e a regra de sudo." >&2
  exit 14
fi
exit $CODE
