#!/usr/bin/env bash
# ════════════════════════════════════════════════════════════════════════════
# IDros bootstrap — ETAPA A do provisionamento (Fleet 2 etapas, 2026-07-08)
# ════════════════════════════════════════════════════════════════════════════
#
# Instala IDros num Rock zerado e coloca o robô no ORFANATO — tenant builtin
# da IDtrack onde robôs sem cliente aguardam adoção. A Etapa B (adoção) é
# feita depois via Hub: escolhe tenant + location e o robô se reprovisiona
# sozinho (manifest + mapas + tailnet do tenant), sem ninguém tocar nele.
#
# Fluxo:
#   1. Instala deps de sistema
#   2. Apresenta o bootstrap token ao Fleet → recebe o pacote da Etapa A
#      (identidade do robô, preauthkey da tailnet, creds MQTT do orfanato)
#   3. Instala tailscale + entra na tailnet (user __orphanage__)
#   4. Baixa IDros tarball do artifact server VIA TAILNET
#   5. Extrai + venv + deps Python
#   6. Manifest do orfanato (com provisioning.bootstrap_token pro announce)
#   7. Sudoers drop-in (reprovisionamento precisa de tailscale up + restart)
#   8. systemd → IDros sobe automático no boot
#
# USO (no Rock zerado com Ubuntu/Debian + acesso root):
#   1. Operador registra o robô no Hub (POST /fleet/robots) → recebe o token
#   2. curl -fsSL https://bootstrap.idtrack.net | sudo bash -s -- \
#          --bootstrap-token="<token>"
#
# Args opcionais:
#   --hostname=<nome>           hostname na tailnet (default: robot_id do Fleet)
#   --fleet-api=<url>           API do Fleet (default: https://api.idtrack.net)
#   --idros-version=<version>   pin de versão (default: latest)
#   --headscale-url=<url>       servidor Headscale (default: nosso)
#   --artifact-server=<url>     artifact server (default: http://100.64.0.1:8080)
#   --serial-device=<dev>       device serial do chassi (default: /dev/reeman,
#                               symlink udev estável instalado por este script)
#   --rfid-net-ip=<cidr>        IP estático na rede-ilha do switch do leitor RFID
#                               (ex: 192.168.72.50/24). USE UM RANGE DEDICADO,
#                               DIFERENTE do WiFi do local (ex: 192.168.72.0/24),
#                               senão os 2 segmentos colidem (ARP flux). Opt-in:
#                               sem isso, não configura eth nenhuma. Ver 1c/1e.
#   --rfid-net-iface=<iface>    interface eth no switch (default: auto-detecta a
#                               1ª ethernet com fio via nmcli)
#   --rfid-reader-ip=<ip>       IP do reader nessa ilha (ex: 192.168.72.174)

set -euo pipefail

# ─── Defaults ───────────────────────────────────────────────────────────────
FLEET_API="https://api.idtrack.net"
HEADSCALE_URL="https://headscale.idtrack.net"
ARTIFACT_SERVER="http://100.64.0.1:8080"
IDROS_VERSION="latest"
BOOTSTRAP_TOKEN=""
HOSTNAME_OVERRIDE=""
SERIAL_DEVICE="/dev/reeman"   # symlink udev estável (regra instalada na seção 1b)
RFID_NET_IP=""                # ex: 192.168.72.50/24 — IP do Rock na ilha do reader (RANGE DEDICADO ≠ WiFi; opt-in)
RFID_NET_IFACE=""             # interface eth no switch (default: auto-detect via nmcli)
RFID_READER_IP=""             # ex: 192.168.72.174 — IP do reader na ilha
MAINBOARD_ETH_IP="192.168.72.60"  # IP da eth (USB) da mainboard Reeman na ilha — gw dela = o Rock (NAT, seção 1e)
ADMIN_SUDO_PASSWORD="${ADMIN_SUDO_PASSWORD:-}"  # senha sudo padrão (rock + user idtrack); vazio=não mexe. Passe via env/flag — NUNCA no git.

INSTALL_DIR="/opt/idros"
MANIFEST_PATH="${INSTALL_DIR}/manifest.json"

# ─── Parse args ─────────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
  case "$1" in
    --bootstrap-token=*) BOOTSTRAP_TOKEN="${1#*=}"; shift ;;
    --bootstrap-token)   BOOTSTRAP_TOKEN="$2"; shift 2 ;;
    --hostname=*)        HOSTNAME_OVERRIDE="${1#*=}"; shift ;;
    --hostname)          HOSTNAME_OVERRIDE="$2"; shift 2 ;;
    --fleet-api=*)       FLEET_API="${1#*=}"; shift ;;
    --fleet-api)         FLEET_API="$2"; shift 2 ;;
    --idros-version=*)   IDROS_VERSION="${1#*=}"; shift ;;
    --idros-version)     IDROS_VERSION="$2"; shift 2 ;;
    --headscale-url=*)   HEADSCALE_URL="${1#*=}"; shift ;;
    --headscale-url)     HEADSCALE_URL="$2"; shift 2 ;;
    --artifact-server=*) ARTIFACT_SERVER="${1#*=}"; shift ;;
    --artifact-server)   ARTIFACT_SERVER="$2"; shift 2 ;;
    --serial-device=*)   SERIAL_DEVICE="${1#*=}"; shift ;;
    --serial-device)     SERIAL_DEVICE="$2"; shift 2 ;;
    --rfid-net-ip=*)     RFID_NET_IP="${1#*=}"; shift ;;
    --rfid-net-ip)       RFID_NET_IP="$2"; shift 2 ;;
    --rfid-net-iface=*)  RFID_NET_IFACE="${1#*=}"; shift ;;
    --rfid-net-iface)    RFID_NET_IFACE="$2"; shift 2 ;;
    --rfid-reader-ip=*)  RFID_READER_IP="${1#*=}"; shift ;;
    --rfid-reader-ip)    RFID_READER_IP="$2"; shift 2 ;;
    --admin-password=*)  ADMIN_SUDO_PASSWORD="${1#*=}"; shift ;;
    --admin-password)    ADMIN_SUDO_PASSWORD="$2"; shift 2 ;;
    -h|--help)
      sed -n '/^# USO/,/^# Args/p' "$0"
      exit 0 ;;
    *)
      echo "Arg desconhecido: $1" >&2
      exit 1 ;;
  esac
done

# ─── Validação ──────────────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
  echo "ERRO: precisa rodar como root (sudo)." >&2
  exit 1
fi
if [[ -z "${BOOTSTRAP_TOKEN}" ]]; then
  echo "ERRO: --bootstrap-token obrigatório." >&2
  echo "  Registre o robô no Hub (Fleet → Robôs → Registrar) e cole o" >&2
  echo "  comando gerado, ou passe o token manualmente." >&2
  exit 1
fi

echo "═══════════════════════════════════════════════════════════════════"
echo "IDros bootstrap — Etapa A (orfanato)"
echo "═══════════════════════════════════════════════════════════════════"
echo "  Fleet API:       ${FLEET_API}"
echo "  Headscale:       ${HEADSCALE_URL}"
echo "  Artifact server: ${ARTIFACT_SERVER}"
echo "  IDros version:   ${IDROS_VERSION}"
echo "  Install dir:     ${INSTALL_DIR}"
echo "═══════════════════════════════════════════════════════════════════"
echo ""

# ─── 1. Instala deps de sistema ─────────────────────────────────────────────
echo "→ [1/8] Instalando dependências de sistema (apt)..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq \
  python3 python3-venv python3-pip \
  curl ca-certificates jq \
  > /dev/null
echo "✓ deps instaladas"

# ─── 1b. Regra udev: /dev/reeman estável pro chassi ─────────────────────────
# O nome /dev/ttyUSBn muda quando o adaptador USB-serial cai e volta (cabo,
# glitch de energia, reboot). Um symlink fixo /dev/reeman → o adaptador PL2303
# do chassi torna o manifest imune a isso. Ver infra/idros/99-idtrack-reeman.rules.
echo "→ [1b] Instalando regra udev /dev/reeman..."
cat > /etc/udev/rules.d/99-idtrack-reeman.rules <<'UDEV'
# /dev/reeman — symlink estável pro USB-serial do chassi Reeman (PL2303).
# Gerado por infra/idros/bootstrap.sh. Detalhes: infra/idros/99-idtrack-reeman.rules.
SUBSYSTEM=="tty", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SYMLINK+="reeman", GROUP="dialout", MODE="0660"
UDEV
udevadm control --reload-rules 2>/dev/null || true
udevadm trigger --subsystem-match=tty 2>/dev/null || true
if [ -e /dev/reeman ]; then
  echo "✓ /dev/reeman → $(readlink -f /dev/reeman)"
else
  echo "⚠ /dev/reeman ainda não presente (chassi desligado/desconectado?). O"
  echo "  IDros reconecta sozinho quando o adaptador aparecer."
fi

# ─── 1c. Rede-ilha do leitor RFID (switch dedicado) ─────────────────────────
# Setup: reader Impinj (R700), a eth da mainboard Reeman e o Rock no MESMO
# switch, numa REDE-ILHA com RANGE DEDICADO (ex: 192.168.72.0/24) — SEPARADO do
# WiFi do local. IMPORTANTE (aprendido no campo 2026-08-04): se a ilha usar o
# MESMO range do WiFi (ex: ambos 192.168.1.0/24), os dois segmentos colidem —
# a mainboard fica dual-homed no mesmo /24 (WiFi + cabo) e dá ARP flux (hosts
# não sabem qual porta usar; o robô "some" da rede). Range dedicado resolve.
# A eth do Rock recebe um IP estático nessa ilha pra alcançar o reader (sem
# isso: "No route to host"). Opt-in via --rfid-net-ip. Persiste no reboot
# (NetworkManager, autoconnect). never-default: a ilha NÃO é rota default do
# Rock (ele sai pela WiFi/tailnet).
if [[ -n "${RFID_NET_IP}" ]]; then
  echo ""
  echo "→ [1c] Configurando rede do leitor RFID (${RFID_NET_IP})..."
  if ! command -v nmcli &> /dev/null; then
    echo "⚠ nmcli não encontrado — pulei a config da rede do reader."
    echo "  (Rock usa NetworkManager; configure a eth manualmente se preciso.)"
  else
    IFACE="${RFID_NET_IFACE}"
    if [[ -z "${IFACE}" ]]; then
      IFACE=$(nmcli -t -f DEVICE,TYPE device 2>/dev/null | awk -F: '$2=="ethernet"{print $1; exit}')
      echo "  interface auto-detectada: ${IFACE:-<nenhuma>}"
    fi
    if [[ -z "${IFACE}" ]]; then
      echo "⚠ nenhuma interface ethernet com fio encontrada — pulei a config."
    else
      # Rotas /32 via eth pro reader E pra mainboard (num /24 plano, força o
      # tráfego deles pela eth — senão o Rock tenta pela WiFi e falha quando o
      # WiFi da mainboard cai).
      ROUTES=""
      [[ -n "${RFID_READER_IP}" ]] && ROUTES="${RFID_READER_IP}/32"
      [[ -n "${MAINBOARD_ETH_IP}" ]] && ROUTES="${ROUTES:+${ROUTES},}${MAINBOARD_ETH_IP%%/*}/32"
      ROUTES_ARG=()
      [[ -n "${ROUTES}" ]] && ROUTES_ARG=(ipv4.routes "${ROUTES}")
      nmcli con delete impinj-net 2>/dev/null || true   # idempotente (re-bootstrap)
      nmcli con add type ethernet ifname "${IFACE}" con-name impinj-net \
        ipv4.method manual ipv4.addresses "${RFID_NET_IP}" \
        ipv4.never-default yes ipv4.route-metric 700 \
        "${ROUTES_ARG[@]}" \
        ipv6.method disabled \
        connection.autoconnect yes
      nmcli con up impinj-net 2>/dev/null || true
      echo "✓ eth ${IFACE} = ${RFID_NET_IP} (con impinj-net; autoconnect; never-default)"
    fi
  fi
fi

# ─── 1d. Proxy web da mainboard + alias de nome (padrão) ────────────────────
# A mainboard Reeman fica na eth (MAINBOARD_ETH_IP); com o WiFi dela desligado
# (padrão no campo — sai pela internet do Rock), ela só é alcançável PELO Rock.
# Este proxy (socat) expõe a web/API da mainboard nas interfaces do Rock
# (WiFi/tailnet) na :8090 → Mac e suporte remoto chegam via Rock. Alias
# 'mainboard'/'robo' no /etc/hosts pro lado do Rock referir por nome.
if [[ -n "${RFID_NET_IP}" && -n "${MAINBOARD_ETH_IP}" ]]; then
  MB_IP="${MAINBOARD_ETH_IP%%/*}"
  echo ""
  echo "→ [1d] Proxy web da mainboard (${MB_IP}:80 → Rock:8090)..."
  if command -v socat &> /dev/null || apt-get install -y socat >/dev/null 2>&1; then
    cat > /etc/systemd/system/idros-mainboard-proxy.service <<UNIT
[Unit]
Description=IDros mainboard web proxy (Rock -> mainboard eth ${MB_IP}:80)
After=network-online.target

[Service]
ExecStart=/usr/bin/socat TCP-LISTEN:8090,fork,reuseaddr TCP:${MB_IP}:80
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
UNIT
    systemctl daemon-reload
    systemctl enable --now idros-mainboard-proxy.service 2>/dev/null || true
    echo "✓ proxy da mainboard em :8090 (systemd idros-mainboard-proxy)"
  else
    echo "⚠ socat indisponível — pulei o proxy da mainboard."
  fi
  grep -q "reeman-mainboard" /etc/hosts 2>/dev/null || \
    echo "${MB_IP} mainboard robo reeman-mainboard" >> /etc/hosts
fi

# ─── 1e. Rock = gateway/NAT da ilha (internet p/ a mainboard) ───────────────
# Com a WiFi da mainboard desligada (alvo do piloto), a mainboard sai pra
# internet PELO cabo → o Rock → WiFi/tailnet. Pra isso o Rock roteia+NATeia a
# rede-ilha. Assim o suporte remoto da Reeman (túnel de saída da mainboard,
# frp p/ o servidor deles) continua funcionando atrás do nosso NAT, sem WiFi e
# sem abrir porta de entrada. A mainboard precisa ter o gateway default = IP do
# Rock na ilha (${RFID_NET_IP%%/*}) — isso é config NA mainboard (feita pela
# Reeman; a gente não tem shell nela). Validado no campo 2026-08-04.
if [[ -n "${RFID_NET_IP}" ]]; then
  ISLAND_NET="$(echo "${RFID_NET_IP%%/*}" | awk -F. '{print $1"."$2"."$3".0/24"}')"
  echo ""
  echo "→ [1e] NAT da ilha ${ISLAND_NET} (Rock = gateway de internet)..."
  sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1 || true
  grep -q "net.ipv4.ip_forward" /etc/sysctl.d/99-idtrack-nat.conf 2>/dev/null || \
    echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-idtrack-nat.conf
  # MASQUERADE: reescreve a origem da ilha na saída (WiFi/tailnet).
  iptables -t nat -C POSTROUTING -s "${ISLAND_NET}" ! -d "${ISLAND_NET}" -j MASQUERADE 2>/dev/null || \
    iptables -t nat -A POSTROUTING -s "${ISLAND_NET}" ! -d "${ISLAND_NET}" -j MASQUERADE
  # FORWARD ACCEPT: o Docker no Rock põe a policy da cadeia FORWARD em DROP e
  # gerencia o iptables — SEM estas regras, o encaminhamento ilha→WiFi é
  # DROPADO antes do POSTROUTING e a mainboard NÃO alcança a internet (o túnel
  # de suporte da Reeman pelo cabo não sobe). Idempotente.
  iptables -C FORWARD -s "${ISLAND_NET}" -j ACCEPT 2>/dev/null || \
    iptables -A FORWARD -s "${ISLAND_NET}" -j ACCEPT
  iptables -C FORWARD -d "${ISLAND_NET}" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
    iptables -A FORWARD -d "${ISLAND_NET}" -m state --state RELATED,ESTABLISHED -j ACCEPT
  # MSS clamp: o uplink do local (WiFi/roteador) às vezes tem MTU < 1500. Sem
  # isto, o TCP encaminhado da ilha negocia MSS de 1500 e os pacotes GRANDES são
  # descartados em silêncio (DF + PMTUD quebrado) — o controle passa mas transfer
  # trava. Pegou o suporte remoto da Reeman (frp): "vejo o robô mas a interface
  # não carrega". set-mss 1400 (não clamp-to-pmtu: nossa iface é 1500, o gargalo
  # é upstream). Ver robot-network-topology.md.
  iptables -t mangle -C FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1400 2>/dev/null || \
    iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1400
  # Persiste. Instala o pacote se faltar (espelha o padrão do socat na 1d) —
  # senão as regras somem no reboot (ex: queda de energia no hospital).
  command -v netfilter-persistent &> /dev/null || \
    DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent >/dev/null 2>&1 || true
  if command -v netfilter-persistent &> /dev/null; then
    netfilter-persistent save >/dev/null 2>&1 || true
    echo "✓ NAT ${ISLAND_NET} ativo + FORWARD + persistido (netfilter-persistent)"
  else
    echo "⚠ netfilter-persistent indisponível — regras ativas mas NÃO persistem no reboot"
  fi
fi

# ─── 2. Apresenta o token ao Fleet → pacote da Etapa A ──────────────────────
# Endpoint PÚBLICO (via Caddy) — o robô ainda NÃO está na tailnet; o
# preauthkey vem na resposta. Auth pelo bootstrap token one-time.
echo ""
echo "→ [2/8] Apresentando bootstrap token ao Fleet..."
HOSTNAME_ARG="${HOSTNAME_OVERRIDE:-$(hostname)}"
BOOTSTRAP_RESPONSE=$(curl -fsS -X POST \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"${BOOTSTRAP_TOKEN}\",\"hostname\":\"${HOSTNAME_ARG}\"}" \
  --max-time 30 \
  "${FLEET_API}/api/v1/fleet/provisioning/bootstrap")

ROBOT_ID=$(echo "${BOOTSTRAP_RESPONSE}"        | jq -r '.robot_id')
ROBOT_TYPE=$(echo "${BOOTSTRAP_RESPONSE}"      | jq -r '.robot_type')
TENANT_ID=$(echo "${BOOTSTRAP_RESPONSE}"       | jq -r '.tenant_id')
TS_AUTHKEY=$(echo "${BOOTSTRAP_RESPONSE}"      | jq -r '.headscale_preauthkey')
MQTT_HOST=$(echo "${BOOTSTRAP_RESPONSE}"       | jq -r '.mqtt.mqtt_host')
MQTT_PORT=$(echo "${BOOTSTRAP_RESPONSE}"       | jq -r '.mqtt.mqtt_port')
MQTT_USER=$(echo "${BOOTSTRAP_RESPONSE}"       | jq -r '.mqtt.mqtt_user')
MQTT_PASSWORD=$(echo "${BOOTSTRAP_RESPONSE}"   | jq -r '.mqtt.mqtt_password')

if [[ -z "${ROBOT_ID}" || "${ROBOT_ID}" == "null" ]]; then
  echo "ERRO: resposta do Fleet inválida:" >&2
  echo "${BOOTSTRAP_RESPONSE}" >&2
  exit 1
fi
HOSTNAME_OVERRIDE="${HOSTNAME_OVERRIDE:-${ROBOT_ID}}"
echo "✓ Pacote recebido: robot_id=${ROBOT_ID} type=${ROBOT_TYPE} (orfanato)"

# ─── 3. Instala tailscale + entra na tailnet ────────────────────────────────
echo ""
echo "→ [3/8] Instalando tailscale + conectando na tailnet (orfanato)..."
if ! command -v tailscale &> /dev/null; then
  curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg \
    | tee /usr/share/keyrings/tailscale-archive-keyring.gpg > /dev/null
  curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list \
    | tee /etc/apt/sources.list.d/tailscale.list > /dev/null
  apt-get update -qq
  apt-get install -y -qq tailscale > /dev/null
  echo "✓ tailscale instalado: $(tailscale version | head -1)"
else
  echo "✓ tailscale já instalado: $(tailscale version | head -1)"
fi
systemctl enable --now tailscaled

tailscale up \
  --login-server="${HEADSCALE_URL}" \
  --authkey="${TS_AUTHKEY}" \
  --hostname="${HOSTNAME_OVERRIDE}" \
  --accept-routes=false \
  --reset

TS_IP=$(tailscale ip -4)
echo "✓ Tailnet IP: ${TS_IP}"

# ─── 4. Baixa IDros tarball via tailnet ─────────────────────────────────────
echo ""
echo "→ [4/8] Baixando IDros via tailnet..."
if [[ "${IDROS_VERSION}" == "latest" ]]; then
  LATEST_JSON=$(curl -fsSL "${ARTIFACT_SERVER}/idros/latest.json")
  IDROS_VERSION=$(echo "${LATEST_JSON}" | jq -r '.current_version')
  TARBALL_URL=$(echo "${LATEST_JSON}" | jq -r '.tarball_url')
  EXPECTED_SHA=$(echo "${LATEST_JSON}" | jq -r '.sha256')
else
  TARBALL_URL="${ARTIFACT_SERVER}/idros/idros-${IDROS_VERSION}.tar.gz"
  EXPECTED_SHA=""
fi
echo "  Versão: ${IDROS_VERSION}"
echo "  URL:    ${TARBALL_URL}"

TMP_TARBALL="/tmp/idros-${IDROS_VERSION}.tar.gz"
curl -fsSL "${TARBALL_URL}" -o "${TMP_TARBALL}"

if [[ -n "${EXPECTED_SHA}" ]]; then
  GOT_SHA=$(sha256sum "${TMP_TARBALL}" | awk '{print $1}')
  if [[ "${GOT_SHA}" != "${EXPECTED_SHA}" ]]; then
    echo "ERRO: sha256 mismatch" >&2
    echo "  Esperado: ${EXPECTED_SHA}" >&2
    echo "  Recebido: ${GOT_SHA}" >&2
    exit 1
  fi
  echo "✓ sha256 OK"
fi

# ─── 5. Extrai + venv + deps Python ─────────────────────────────────────────
echo ""
echo "→ [5/8] Extraindo em ${INSTALL_DIR} + venv..."
mkdir -p "${INSTALL_DIR}"
tar -xzf "${TMP_TARBALL}" -C "${INSTALL_DIR}"
rm "${TMP_TARBALL}"
python3 -m venv "${INSTALL_DIR}/.venv"
"${INSTALL_DIR}/.venv/bin/pip" install -q --upgrade pip
"${INSTALL_DIR}/.venv/bin/pip" install -q \
  -r "${INSTALL_DIR}/apps/ros/requirements.txt" \
  -e "${INSTALL_DIR}/python-libs/capability-sdk"
echo "✓ IDros ${IDROS_VERSION} + venv prontos"

# ─── 6. Manifest do orfanato ────────────────────────────────────────────────
# Robô órfão: sem mapas, sem location — só identidade + MQTT do orfanato +
# provisioning.bootstrap_token (pro IDros anunciar o cortex_id ao Fleet).
# O manifest DEFINITIVO chega na adoção via reprovisionamento (top-down,
# Fleet é a fonte da verdade).
echo ""
echo "→ [6/8] Configurando manifest (orfanato)..."
if [[ "${ROBOT_TYPE}" == "fake" ]]; then
  CONNECTION_TYPE="http"
else
  CONNECTION_TYPE="serial"
fi

if [[ -f "${MANIFEST_PATH}" ]]; then
  # Re-bootstrap: preserva o resto, atualiza cortex.* + provisioning
  echo "✓ manifest existe — atualizando cortex.* + provisioning..."
  python3 <<PYEOF
import json
p = "${MANIFEST_PATH}"
m = json.load(open(p))
m.setdefault("cortex", {})
m["cortex"]["mqtt_host"] = "${MQTT_HOST}"
m["cortex"]["mqtt_port"] = ${MQTT_PORT}
m["cortex"]["mqtt_user"] = "${MQTT_USER}" or None
m["cortex"]["mqtt_password"] = "${MQTT_PASSWORD}" or None
m["cortex"]["tenant_id"] = "${TENANT_ID}"
m["provisioning"] = {
    "fleet_api_url": "${FLEET_API}",
    "bootstrap_token": "${BOOTSTRAP_TOKEN}",
}
json.dump(m, open(p, "w"), indent=2, ensure_ascii=False)
print(f"  ✓ cortex + provisioning atualizados (user={m['cortex']['mqtt_user']})")
PYEOF
else
  cat > "${MANIFEST_PATH}" <<EOF
{
  "robot": {
    "id": "${ROBOT_ID}",
    "name": "${HOSTNAME_OVERRIDE}",
    "type": "${ROBOT_TYPE}",
    "cortex_id": null
  },
  "cortex": {
    "mqtt_host": "${MQTT_HOST}",
    "mqtt_port": ${MQTT_PORT},
    "mqtt_user": "${MQTT_USER}",
    "mqtt_password": "${MQTT_PASSWORD}",
    "tenant_id": "${TENANT_ID}"
  },
  "provisioning": {
    "fleet_api_url": "${FLEET_API}",
    "bootstrap_token": "${BOOTSTRAP_TOKEN}"
  },
  "os": {
    "telemetry_api_port": 8099,
    "heartbeat_interval": 30,
    "pose_interval": 2
  },
  "capabilities": [],
  "meeting_points": [],
  "safety": {
    "battery_minimum": 15,
    "connectivity_timeout": 120,
    "emergency_button_inverted": true
  },
  "timeouts": {
    "zone_navigation": 180,
    "zone_scan": 300,
    "return_to_base": 240
  },
  "hardware": {
    "connection_type": "${CONNECTION_TYPE}",
    "serial_device": "${SERIAL_DEVICE}",
    "serial_baud": 115200,
    "api_url": "",
    "charge_flag_docked": 2,
    "charge_flag_docking": 8,
    "map_to_location": {},
    "discovered_maps": [],
    "rfid_reader": null
  }
}
EOF
  echo "✓ manifest criado em ${MANIFEST_PATH}"
fi
chmod 600 "${MANIFEST_PATH}"

mkdir -p "${INSTALL_DIR}/identity" "${INSTALL_DIR}/maps"

# ─── 7. User + sudoers pro reprovisionamento ────────────────────────────────
echo ""
echo "→ [7/8] User idros + sudoers do reprovisionamento..."
if ! id -u idros &> /dev/null; then
  useradd --system --no-create-home --shell /usr/sbin/nologin idros
fi
# Grupos: journald logs (admin_server) + dialout (serial /dev/ttyUSB*)
usermod -a -G systemd-journal,adm,dialout idros
chown -R idros:idros "${INSTALL_DIR}"

# O reprovisionamento (adoção) precisa re-autenticar a tailnet no user do
# tenant destino e reiniciar o IDros — ambos exigem root. Permitimos
# EXATAMENTE esses comandos, nada além.
TAILSCALE_BIN=$(command -v tailscale)
SYSTEMCTL_BIN=$(command -v systemctl)
BASH_BIN=$(command -v bash)

# Script de update remoto num caminho ESTÁVEL (fora de /opt/idros, que ele
# mesmo sobrescreve). O admin_server chama via `sudo /usr/local/bin/idros-update`.
install -m 755 "${INSTALL_DIR}/apps/ros/scripts/update-idros.sh" /usr/local/bin/idros-update

cat > /etc/sudoers.d/idros-provisioning <<SUDOEOF
# IDros reprovisionamento (Fleet adoção) + operação remota — gerado pelo bootstrap.sh
idros ALL=(root) NOPASSWD: ${TAILSCALE_BIN} up *
idros ALL=(root) NOPASSWD: ${SYSTEMCTL_BIN} restart idros
idros ALL=(root) NOPASSWD: ${BASH_BIN} -c sleep 2 && systemctl restart idros
# Atualizar IDros / reiniciar o host pela tela (admin_server /update, /reboot-host)
idros ALL=(root) NOPASSWD: /usr/local/bin/idros-update
idros ALL=(root) NOPASSWD: ${SYSTEMCTL_BIN} reboot
SUDOEOF
chmod 440 /etc/sudoers.d/idros-provisioning
echo "✓ sudoers drop-in + update-script instalados"

# ─── 7b. Usuário admin sudo + hardening de SSH (opt-in) ─────────────────────
# Padroniza o acesso do robô: cria o user 'idtrack' (sudo, admin não-default),
# define a senha sudo de 'rock' e 'idtrack', e DESABILITA login root por SSH.
# Opt-in via ADMIN_SUDO_PASSWORD (env ou --admin-password) — a senha NUNCA fica
# no git. Sem a var, não mexe em usuário/senha/ssh.
if [[ -n "${ADMIN_SUDO_PASSWORD}" ]]; then
  echo ""
  echo "→ [7b] User admin sudo (idtrack) + senha sudo + hardening de SSH..."
  id -u idtrack &>/dev/null || useradd -m -s /bin/bash idtrack
  usermod -aG sudo idtrack
  echo "idtrack:${ADMIN_SUDO_PASSWORD}" | chpasswd
  id -u rock &>/dev/null && echo "rock:${ADMIN_SUDO_PASSWORD}" | chpasswd
  # desabilita login root por SSH (valida a config ANTES de recarregar)
  mkdir -p /etc/ssh/sshd_config.d
  echo 'PermitRootLogin no' > /etc/ssh/sshd_config.d/99-idtrack-hardening.conf
  if sshd -t; then
    systemctl reload ssh 2>/dev/null || systemctl reload sshd 2>/dev/null || true
    echo "✓ user idtrack + senha sudo (rock/idtrack) + root SSH desabilitado"
  else
    echo "⚠ sshd -t falhou — removi o drop-in pra não quebrar o SSH"
    rm -f /etc/ssh/sshd_config.d/99-idtrack-hardening.conf
  fi
fi

# ─── 8. systemd ─────────────────────────────────────────────────────────────
echo ""
echo "→ [8/8] Configurando systemd..."
install -m 644 "${INSTALL_DIR}/apps/ros/systemd/idros.service" /etc/systemd/system/idros.service

systemctl daemon-reload
systemctl enable idros.service
systemctl restart idros.service

sleep 3
if systemctl is-active --quiet idros.service; then
  echo "✓ idros.service rodando (PID $(systemctl show idros -p MainPID --value))"
else
  echo "⚠ idros.service não subiu — logs:"
  journalctl -u idros --no-pager -n 30
  exit 1
fi

# ─── 8b. Sidecar RFID LLRP (Impinj R700) ────────────────────────────────────
# O sidecar C++ fala LLRP com o reader e expõe um UDS que o driver Python do
# IDros consome. O binário (arm32 estático) linka libs LTK PROPRIETÁRIAS (EULA
# Impinj), é gitignored e NÃO cabe num secret (~4.7MB) → fica no ARTIFACT SERVER
# e é baixado aqui (best-effort). Rebuild+upload quando o .cpp mudar (ver runbook
# impinj-sidecar-build-e-secret.md). Só instala o serviço SE o binário existir.
SIDECAR_BIN="${INSTALL_DIR}/apps/ros/impinj-sidecar/impinj-sidecar"
SIDECAR_UNIT="${INSTALL_DIR}/apps/ros/impinj-sidecar/idros-impinj-sidecar.service"
echo ""
echo "→ [8b] Sidecar RFID LLRP (Impinj)..."
if curl -fsSL "${ARTIFACT_SERVER}/idros/impinj-sidecar" -o "${SIDECAR_BIN}.new" 2>/dev/null; then
  mv "${SIDECAR_BIN}.new" "${SIDECAR_BIN}"
  echo "  ✓ binário baixado de ${ARTIFACT_SERVER}/idros/impinj-sidecar"
else
  rm -f "${SIDECAR_BIN}.new" 2>/dev/null || true
  echo "  ⚠ binário do sidecar indisponível no artifact server — RFID/LLRP fica off"
fi
if [[ -f "${SIDECAR_BIN}" ]]; then
  chmod +x "${SIDECAR_BIN}"
  chown idros:idros "${SIDECAR_BIN}" 2>/dev/null || true
  # Env com o IP do reader. Cria se não existe; se re-bootstrap com --rfid-reader-ip
  # explícito, atualiza a linha; senão preserva o que já está lá.
  mkdir -p /etc/idros
  if [[ ! -f /etc/idros/impinj-sidecar.env ]]; then
    echo "READER_IP=${RFID_READER_IP}" > /etc/idros/impinj-sidecar.env
    chmod 644 /etc/idros/impinj-sidecar.env
    echo "  ✓ /etc/idros/impinj-sidecar.env criado (READER_IP=${RFID_READER_IP:-<vazio>})"
  elif [[ -n "${RFID_READER_IP}" ]]; then
    if grep -q '^READER_IP=' /etc/idros/impinj-sidecar.env; then
      sed -i "s|^READER_IP=.*|READER_IP=${RFID_READER_IP}|" /etc/idros/impinj-sidecar.env
    else
      echo "READER_IP=${RFID_READER_IP}" >> /etc/idros/impinj-sidecar.env
    fi
    echo "  ✓ /etc/idros/impinj-sidecar.env atualizado (READER_IP=${RFID_READER_IP})"
  else
    echo "  ✓ /etc/idros/impinj-sidecar.env preservado ($(grep '^READER_IP=' /etc/idros/impinj-sidecar.env || echo READER_IP=?))"
  fi
  install -m 644 "${SIDECAR_UNIT}" /etc/systemd/system/idros-impinj-sidecar.service
  systemctl daemon-reload
  systemctl enable idros-impinj-sidecar.service 2>/dev/null || true
  # Só sobe agora se há um READER_IP setado — sem ele o sidecar só churna
  # (Restart=always). Com o IP vazio, deixa habilitado pro próximo boot/adoção.
  EFFECTIVE_READER_IP="$(sed -n 's/^READER_IP=//p' /etc/idros/impinj-sidecar.env 2>/dev/null | head -1)"
  if [[ -n "${EFFECTIVE_READER_IP}" ]]; then
    systemctl restart idros-impinj-sidecar.service 2>/dev/null || true
    sleep 2
    if systemctl is-active --quiet idros-impinj-sidecar.service; then
      echo "✓ idros-impinj-sidecar.service rodando (reader ${EFFECTIVE_READER_IP})"
    else
      echo "⚠ idros-impinj-sidecar.service não subiu (reader offline/modo REST?). Logs: journalctl -u idros-impinj-sidecar"
    fi
  else
    echo "⚠ READER_IP vazio — serviço habilitado mas NÃO iniciado. Preencha"
    echo "  /etc/idros/impinj-sidecar.env e rode: systemctl start idros-impinj-sidecar"
  fi
else
  echo "⚠ binário do sidecar ausente (${SIDECAR_BIN}) — robô SEM RFID/LLRP."
  echo "  O CI injeta o binário no tarball via secret RFID_SIDECAR_BIN_B64"
  echo "  (ver runbook impinj-sidecar-build-e-secret.md)."
fi

# ─── Pronto ─────────────────────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════════════════"
echo "✓ Etapa A concluída — robô no ORFANATO, aguardando adoção"
echo "═══════════════════════════════════════════════════════════════════"
echo ""
echo "Tailnet IP:    ${TS_IP}"
echo "Robot ID:      ${ROBOT_ID}"
echo "IDros version: ${IDROS_VERSION}"
echo "Logs:          sudo journalctl -u idros -f"
echo ""
echo "Próximo passo (Etapa B): no Hub, Fleet → Orfanato → Adotar."
echo "O robô recebe manifest + mapas do tenant e se reprovisiona sozinho."