#!/bin/bash
# Healthcheck da stack: alerta no Telegram se algo crítico cair. Cooldown de 1h por problema.
#
# FONTE ÚNICA: este arquivo é versionado em infra/vps/healthcheck.sh. O que roda em
# /opt/ultimo/healthcheck.sh é uma cópia instalada por infra/vps/instalar.sh. Editar direto na VPS
# volta a produzir o que havia em 11/08/2026: três .bak do mesmo script, nenhum com histórico.
#
# HEALTHCHECK_DRYRUN=1 imprime o alerta em vez de mandar no Telegram. Use sempre depois de mexer,
# senão um falso positivo novo vira mensagem de verdade no celular do dono.

set -uo pipefail

# Segredos ficam FORA do git, em /etc/sino-ia/healthcheck.env (root:root 0600):
#   BOT=...   token do bot do Telegram
#   CHAT=...  id do destinatário
#   EVO_APIKEY=...  apikey do Evolution
ENVFILE=/etc/sino-ia/healthcheck.env
if [ -r "$ENVFILE" ]; then
  # shellcheck source=/dev/null
  . "$ENVFILE"
else
  echo "healthcheck: $ENVFILE ausente ou ilegível — sem como alertar" >&2
  exit 78                                  # EX_CONFIG: o systemd marca a unidade como failed
fi
: "${BOT:?BOT não definido em $ENVFILE}"
: "${CHAT:?CHAT não definido em $ENVFILE}"
: "${EVO_APIKEY:?EVO_APIKEY não definido em $ENVFILE}"

STATE=/opt/ultimo/health.state
ORQ_ENV=/home/ultimo/claude-workspace/sino-ia/orquestrador/.env
touch "$STATE"
FAIL=""
falhou() { FAIL="$FAIL%0A- $1"; }

# --- systemd ---
# sinoia-mcp-clinica entrou em 11/08/2026: é quem dá preço, disponibilidade e agendamento para a
# Célia, e estava de fora da lista enquanto o alarme dizia que tudo estava bem.
for s in n8n caddy docker sinoia-orquestrador sinoia-mcp-clinica sinoia-mcp-beatriz \
         ooeirense-screenshot ooeirense-fileserver; do
  systemctl is-active --quiet "$s" || falhou "servico $s DOWN"
done

# --- containers ---
docker inspect -f '{{.State.Running}}' beatriz-evolution 2>/dev/null | grep -q true || falhou "Evolution (Docker) DOWN"
docker inspect -f '{{.State.Running}}' beatriz-postgres  2>/dev/null | grep -q true || falhou "Postgres (Docker) DOWN"

# --- n8n responde ---
curl -s -o /dev/null -m 8 http://127.0.0.1:5678/healthz || falhou "n8n healthz sem resposta"

# --- Qual instancia e a Celia? ---
# O nome muda quando a Celia e reconectada pela plataforma (catarina-ia -> celia-l3ibcp em 11/09/2026),
# e o alarme passou a vigiar uma instancia que nao existia mais. Por isso se acha pelo NUMERO.
CELIA_NUM=558994229811
CELIA_INST=$(curl -s -m 10 -H "apikey: $EVO_APIKEY" "http://127.0.0.1:8085/instance/fetchInstances" 2>/dev/null \
  | python3 -c "import sys,json;print(next((x['name'] for x in json.load(sys.stdin) if (x.get('ownerJid') or '').startswith('$CELIA_NUM')),''))" 2>/dev/null)
[ -z "$CELIA_INST" ] && { falhou "nenhuma instancia do Evolution com o numero da Celia ($CELIA_NUM)"; CELIA_INST=celia-l3ibcp; }

# --- WhatsApp conectado ---
# Vigia so as linhas que DEVEM estar no ar. Acrescentar/remover nome nesta linha.
#  beatriz-sino: a Bia, secretaria da Sino.
#  $CELIA_INST: a Celia, cliente em producao (nome resolvido pelo numero, acima).
#  sino-clinica: FORA de proposito; desconectada, alertava sobre um estado intencional.
for inst in beatriz-sino "$CELIA_INST"; do
  st=$(curl -s -m 10 -H "apikey: $EVO_APIKEY" "http://127.0.0.1:8085/instance/connectionState/$inst" 2>/dev/null \
       | python3 -c "import sys,json;print(json.load(sys.stdin)['instance']['state'])" 2>/dev/null)
  [ "$st" = "open" ] || falhou "WhatsApp $inst: ${st:-sem resposta}"
done

# --- O SALTO Evolution -> orquestrador ---
# Esta é a falha de 11/08/2026, e nenhuma checagem acima a enxergava. O webhook da instância foi
# gravado com um ?k= que o orquestrador não conhece; ele passou a devolver 401 ANTES de qualquer
# log, e a Célia ficou 3h25 muda com todo o resto verde: instância `open`, serviço `active`, /health
# 200, MCP respondendo. Peça viva não é o mesmo que caminho vivo. Aqui se confere o caminho.
WSEC=$(grep -oP '(?<=^WEBHOOK_SECRET=).*' "$ORQ_ENV" 2>/dev/null)
if [ -z "$WSEC" ]; then
  falhou "WEBHOOK_SECRET nao encontrado em $ORQ_ENV"
else
  # 1. o que o Evolution está configurado para mandar bate com o que o orquestrador espera?
  KGRAVADO=$(curl -s -m 10 -H "apikey: $EVO_APIKEY" "http://127.0.0.1:8085/webhook/find/$CELIA_INST" 2>/dev/null \
             | python3 -c "import sys,json,urllib.parse as u;print(u.parse_qs(u.urlparse(json.load(sys.stdin).get('url','')).query).get('k',[''])[0])" 2>/dev/null)
  if [ "$KGRAVADO" != "$WSEC" ]; then
    falhou "webhook da $CELIA_INST com segredo DIVERGENTE (gravado ${KGRAVADO:0:6}..., esperado ${WSEC:0:6}...) — a Celia nao recebe mensagem"
  fi
  # 2. e o orquestrador aceita mesmo? (evento inexistente: entra, é aceito e é ignorado)
  CODE=$(curl -s -o /dev/null -w '%{http_code}' -m 10 -X POST \
         "http://127.0.0.1:8787/webhook/evolution?k=$WSEC" \
         -H 'Content-Type: application/json' -d '{"event":"healthcheck.ping"}' 2>/dev/null)
  [ "$CODE" = "200" ] || falhou "webhook do orquestrador devolveu HTTP ${CODE:-sem resposta} para o segredo do .env"
fi

# --- MCP da clinica ---
# NAO basta o servico responder. Em 27/07 ele subiu sem a env FLUXA_CLINIC_ID e devolvia vazio com
# HTTP 200: a Celia ficou sem preco e sem agendar, e ninguem foi avisado. Por isso se confere que
# vem servico DE VERDADE na resposta. Duas tentativas por causa de cold start.
MCPSEC=$(grep -oP '(?<=^MCP_SECRET=).*' "$ORQ_ENV" 2>/dev/null)
MCPURL="http://127.0.0.1:8790/"
MCPOK=""
for _try in 1 2; do
  MCPRESP=$(curl -s -m 25 -X POST "$MCPURL" -H "Content-Type: application/json" -H "x-sino-secret: $MCPSEC" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"listar_servicos","arguments":{"busca":"morfologica"}}}' 2>/dev/null)
  echo "$MCPRESP" | grep -q '"servicos":\[{' && { MCPOK=1; break; }
  sleep 3
done
[ -z "$MCPOK" ] && falhou "MCP da clinica (Fluxa) fora: a Celia esta sem preco e sem agendamento"

# --- disco ---
USE=$(df / | awk 'NR==2{print $5}' | tr -d '%')
[ "${USE:-0}" -gt 90 ] && falhou "disco em ${USE}%"

# --- alerta com cooldown ---
avisar() {
  if [ "${HEALTHCHECK_DRYRUN:-}" = "1" ]; then
    printf 'DRYRUN, mandaria no Telegram:\n%b\n' "${1//%0A/\\n}"
    return
  fi
  curl -s -X POST "https://api.telegram.org/bot$BOT/sendMessage" \
    --data-urlencode "chat_id=$CHAT" --data "text=$1" >/dev/null
}

NOW=$(date +%s)
LAST=$(cat "$STATE" 2>/dev/null || echo 0)
if [ -n "$FAIL" ]; then
  if [ $((NOW - LAST)) -gt 3600 ]; then   # re-alerta no máx a cada 1h
    avisar "🚨 VPS Oeirense com problema(s):$FAIL"
    [ "${HEALTHCHECK_DRYRUN:-}" = "1" ] || echo "$NOW" > "$STATE"
  else
    echo "healthcheck: em falha, dentro do cooldown de 1h:${FAIL//%0A/ }" >&2
  fi
else
  # se estava em falha e voltou, avisa recuperação e zera
  if [ "$LAST" != "0" ]; then
    avisar "✅ VPS Oeirense: todos os serviços normalizados."
    [ "${HEALTHCHECK_DRYRUN:-}" = "1" ] || echo "0" > "$STATE"
  fi
fi
