#!/bin/bash
# mcinstaller.sh v2 - MeshCentral Agent Installer for macOS (Universal: Intel + Apple Silicon)
# Compatible con macOS 15+ Sequoia y macOS 26 Tahoe.
# Usage: bash <(curl -fsSL https://zh.com.pa/mcimac)
#
# Cambios v2 (2026-05-31):
#   - Deteccion automatica de arquitectura (arm64 / x86_64) -> binario nativo
#   - Saltea binary -install (roto en macOS Tahoe, instalaba en /tmp/meshagent)
#   - Instalacion manual: copia binario, extrae msh del JSON embebido, escribe plists
#   - Cachea sudo (evita pipe-to-sudo que produce SIGTRAP en macOS Tahoe)
#   - No codesigna el binario (conserva la firma linker-signed original)

# ============================================
# CLIENT CONFIGURATION
# ============================================
# Formato: "ID|Nombre|MESHID_ENCODED"
# El MESHID_ENCODED es el valor del parametro id= en la URL meshagents
MeshClients=(
    "3DL|3 Dots Logistics|JCEe33rgNXozLdGlMFO08EDCodg\$AGNf@EyV7UGbVcYZ4xA@iP0pemwDt8wKAvYO"
    "360CG|360 Consulting Group|HFsBVemiNwVyfkPfV4KF8qS6BCMzF6rQov\$Xkazf5Iy2sEOZ1hf\$aJJC1wt9Lzlt"
    "360STCG|360 Sterling Tax Consulting Group|JRxNb7O8NpaZX@Zyz@Xk9WfbyvyZQGV62vsNu1lrNFSVUBD17Hq3RHvqQh6ZVTTw"
    "AALN|Arias, Abrego, Lopez & Noriega|FVNN9JTkfE\$B7YEXoCV85B4zTiyheRTFihLuTBfHRqHMvx66QdGP1EYemERNc1ky"
    "ABL|Action Brands Latam|UuRw2OYL2Kl9ahKl2hhnupeK0Lzyr@PvyxIkf0zWOH\$YF@piMSBSTTVomUmx@xnk"
    "ACC|Accman Consulting|b1E29Xu2CUmqFottXWPyCItNZgvpblLoWcBJ9PI0FowoyMJPumcfN2zXEqozDvQ1"
    "AG|Asyr Group|hCHlLZa2xBJhIATDF0tQUv7A\$TIw2UMEJpO8mAMiiWIhWpuQRXAi68ku@SlNUX1z"
    "ATN|Aiming True North|aZm5sYrLFx@rD13DWLwjlqwCsrbRfxVJOL3yBqpP\$LU9mR@3lijnEjXVJ0SlZ8Gt"
    "BGM|Best GlobalMed|vWUglrzeO7Ylfaxe7IrVnhInAIGyqi@HUJXmZ62h90Jnapgj7Z@NqsL6CxXHGvb6"
    "BR|BeRepublic|CCYgaial3eYhwJWCacEthy6\$omyjnaSqXXsRLNjn4aZ2qI\$dFSgZdI\$37@4xRS3r"
    "CDH|Casa Del Helado|UWdht4Kfbodtwts78iYVXVvN1MY5p9iMPNQa8@dkhjlsfU@EQBZhtEZCUlf0WNy8"
    "CI|Clientes Individuales|rY\$u9ynyq4M88AZ58mwv40HKcGGLKXtFqAMcFIKt\$MG8xPgg4iL5qj0dBizM0\$0s"
    "GA|Grupo Andujar|o7uLctTUgOgQ@1avyb24n5v1DwQXEtsHzB0B@isspoEZFTqEoJGNtrfpGGkJfiQS"
    "GC|Globo Consulting|CPsF8hn51w1L1K2Y96Uaj11LVX8EAR2dxM09CSji@J0wMFdV5QOOj5nk2oEZVJeh"
    "GDI|Grupo Desarrollo del Itsmo|7WYf@G9WfmuEqFKtk@o0AS2OGKuB21KRkEMhtQWZEv9n6wbVbhXqDAF@5p2\$Fr0i"
    "FJ|Inter Global Trading / Fundacion Jambo|XdtQzoLOrWgOYzfQktFDDW8xsg8J@EJLYY9JJOcDhsTopWsdike3b900tuvHWf8G"
    "IS507|International Supply 507|uQ2XChdQHBVrPyX6LZa4HmXp6wCBGF\$KnCHFb@MQo@ev4pzPGInnxQFQARbPvaTa"
    "JVA|Jaime Vega Abogados|ylLMY950Z8jmd3wq1T2DBY8l\$dtKV4fWrxi3tTiBxJkSiZebGrNxSTTK6VLiWdj2"
    "KSI|Kol Shearith Israel|cA7RMByrQZXccMrCZ0r3oFRfeETxfM6eXplQSM68mmFHG2eHUYXGFsWaURqwpslR"
    "ND|Nordik Design|BJEj88YWouKOsa50\$Iiv3U1WRBUu0M@qfkrZaS6j\$QqjxKvvJ4\$xTXQweKgrA90h"
    "PGA|Panama Guns & Ammo|lm7OIiVHf4s\$mVIKO@kV6LDoM5akMYvBe0DGkUehGweGEroahX@I3FU5zcrqG3kL"
    "RTA|Reciclaje Total de America|Ij1peqmBTVlUODND0529rKxp6Fu8Sdlmq7XsC\$W46KTJ6zXfV9Dc7IOnVhzPxr8c"
    "STP|Sintec Pharma|ksSldxvC0tQLYtotpG5KnqPUazFegceiwPRK2C9tyaqfYEF4QOGM2lCDMgMFLY1Z"
    "THP|Transmisiones Automaticas PTY|w1OqUEotJIsGxPNBiQ5PdUv@EKG1aQ2V8lug7f1u9KK5Yohw\$8wwy@WIibnwOzrl"
    "TS|Trituracion y Servicios|t8u76\$2QNQaxvObIEjY@@is03Gvi@mXQ475AxGAVZfIapNltvhLUzyuxJYds1nJt"
    "VMC|VM Suppliers|w3xsop9L6FoFspFr8tC06MRjWjvTNn@jdpR9FvpUabloJmg1gbHzUbEUeNcNtOnj"
    "ZH|ZH Consultores|yJiO6y3huGowDbMTQdj0CXXgIW\$O8PM@sD@YWCq2hL8t35m0BtSOHwKJrNuF@BLM"
)

# Servidor MeshCentral
MESH_SERVER_HOST="mc.zhconsultores.com"

# Constantes de instalacion (alineadas con resto del fleet)
COMPANY="ZH Consultores"
SERVICENAME="ZH_Consultores_Remote_Agent"
INSTALL_PARENT="/usr/local/mesh_services"
INSTALL_DIR="${INSTALL_PARENT}/${COMPANY}/${SERVICENAME}"
BIN_NAME="${SERVICENAME}Agent"
BIN="${INSTALL_DIR}/${BIN_NAME}"

# ============================================
# COLORES
# ============================================
CYAN='\033[0;36m'; GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; WHITE='\033[0;37m'; NC='\033[0m'

# ============================================
# FUNCIONES AUXILIARES
# ============================================
get_field() { echo "$1" | cut -d'|' -f"$2"; }

list_clients() {
    echo -e "\n${YELLOW}Clientes disponibles:${NC}\n"
    for client in "${MeshClients[@]}"; do
        local id name
        id=$(get_field "$client" 1)
        name=$(get_field "$client" 2)
        printf "${WHITE}  %-8s - %s${NC}\n" "$id" "$name"
    done
    echo ""
}

find_client() {
    local search="$1"
    for client in "${MeshClients[@]}"; do
        local id
        id=$(echo "$(get_field "$client" 1)" | tr '[:lower:]' '[:upper:]')
        if [[ "$id" == "$search" ]]; then echo "$client"; return 0; fi
    done
    return 1
}

# Detecta arch y devuelve meshinstall ID (29=arm64, 16=x86_64)
detect_meshinstall_id() {
    case "$(uname -m)" in
        arm64)  echo "29" ;;
        x86_64) echo "16" ;;
        *)      echo "29" ;;  # default arm64 (Macs modernas)
    esac
}

# Extrae un valor del MSH JSON embebido en el binario descargado
extract_msh_value() {
    local file="$1" key="$2"
    grep -aoE "\"${key}\":\"[^\"]+\"" "$file" 2>/dev/null | head -1 | sed -E "s/\"${key}\":\"(.*)\"/\1/"
}

cleanup_old_install() {
    # Saca de launchd cualquier registro previo (varias variantes de labels)
    sudo launchctl bootout system/${SERVICENAME} 2>/dev/null || true
    sudo launchctl bootout system/meshagent 2>/dev/null || true
    sudo launchctl bootout "gui/$(id -u)/${SERVICENAME}-launchagent" 2>/dev/null || true
    sudo launchctl bootout "gui/$(id -u)/meshagent-launchagent" 2>/dev/null || true
    # Mata procesos colgados
    sudo pkill -f "${SERVICENAME}Agent" 2>/dev/null || true
    sudo pkill -x meshagent 2>/dev/null || true
    sleep 1
}

# ============================================
# SCRIPT START
# ============================================
clear
echo -e "\n${CYAN}========================================${NC}"
echo -e "${CYAN}  MeshCentral Agent Installer - ZH (v2)${NC}"
echo -e "${CYAN}  macOS Tahoe-compatible ($(uname -m))${NC}"
echo -e "${CYAN}========================================${NC}\n"

[[ "$(uname)" != "Darwin" ]] && { echo -e "${RED}ERROR: Solo macOS.${NC}"; exit 1; }

# Forzar TTY interactivo aunque corra via pipe
if [[ ! -t 0 ]]; then
    if [[ -e /dev/tty ]]; then exec < /dev/tty
    else echo -e "${RED}ERROR: No hay terminal interactiva.${NC}"; exit 1; fi
fi

# Seleccion de cliente
found=false; selectedClient=""
while [[ "$found" == false ]]; do
    read -r -p "Ingrese el Client ID, 'list' para ver todos, o 'exit' para cancelar: " inputID
    inputID=$(echo "$inputID" | tr '[:lower:]' '[:upper:]' | xargs)
    if [[ "$inputID" == "EXIT" ]]; then echo -e "\n${YELLOW}Cancelado.${NC}\n"; exit 0
    elif [[ "$inputID" == "LIST" ]]; then list_clients
    elif [[ -z "$inputID" ]]; then echo -e "${RED}Entrada vacia.${NC}"
    else
        match=$(find_client "$inputID")
        if [[ -n "$match" ]]; then selectedClient="$match"; found=true
        else echo -e "${RED}ID '$inputID' no encontrado.${NC}"; fi
    fi
done

clientID=$(get_field "$selectedClient" 1)
clientName=$(get_field "$selectedClient" 2)
meshID_encoded=$(get_field "$selectedClient" 3)

echo -e "\n${CYAN}========================================${NC}"
echo -e "${GREEN}Cliente: $clientID - $clientName${NC}"
echo -e "${CYAN}Arquitectura: $(uname -m)  -  macOS $(sw_vers -productVersion)${NC}"
echo -e "${CYAN}========================================${NC}\n"

read -r -p "Proceder con la instalacion? (S/N): " confirm
[[ ! "$confirm" =~ ^[YySs] ]] && { echo -e "\n${YELLOW}Cancelado.${NC}\n"; exit 0; }

# Password + cachear sudo (evita pipe-to-sudo)
echo -n "Password: "
read -r -s password; echo ""
if ! echo "$password" | sudo -S -v 2>/dev/null; then
    echo -e "${RED}ERROR: contrasena incorrecta.${NC}"; unset password; exit 1
fi
echo "$password" > ~/.userdata
# Keepalive del sudo timestamp
(while true; do sudo -n -v 2>/dev/null; sleep 30; done) &
SUDO_KEEPALIVE_PID=$!
trap "kill $SUDO_KEEPALIVE_PID 2>/dev/null; unset password" EXIT

# ============================================
# DESCARGA del binario nativo
# ============================================
MESHINSTALL=$(detect_meshinstall_id)
# URL del INSTALLER (binario + JS de menu + JSON msh embebido) - solo para extraer config
URL_INSTALLER="https://${MESH_SERVER_HOST}/meshagents?id=${meshID_encoded}&installflags=2&meshinstall=${MESHINSTALL}"
# URL del AGENT RAW (solo binario sin JS de menu, este es el que corre como servicio)
URL_AGENT="https://${MESH_SERVER_HOST}/meshagents?id=${MESHINSTALL}"
INSTALLER_PATH="/tmp/ZH_MeshInstaller_${clientID}_$$"
AGENT_PATH="/tmp/ZH_MeshAgent_${clientID}_$$"

echo -e "\n${YELLOW}Descargando installer (para extraer config msh)...${NC}"
if ! curl -L --fail --silent --show-error -o "$INSTALLER_PATH" "$URL_INSTALLER"; then
    echo -e "${RED}Error al descargar installer.${NC}"; exit 1
fi
[[ -s "$INSTALLER_PATH" ]] || { echo -e "${RED}Installer vacio.${NC}"; exit 1; }

echo -e "${YELLOW}Descargando agente RAW (meshinstall=${MESHINSTALL})...${NC}"
if ! curl -L --fail --silent --show-error -o "$AGENT_PATH" "$URL_AGENT"; then
    echo -e "${RED}Error al descargar agente.${NC}"; exit 1
fi
[[ -s "$AGENT_PATH" ]] || { echo -e "${RED}Agente vacio.${NC}"; exit 1; }
echo -e "${GREEN}Installer: $(stat -f%z "$INSTALLER_PATH") bytes  /  Agente: $(stat -f%z "$AGENT_PATH") bytes${NC}"

# ============================================
# EXTRAER msh del JSON embebido en el installer
# ============================================
echo -e "\n${YELLOW}Extrayendo configuracion del installer...${NC}"
MSH_MESHSERVER=$(extract_msh_value "$INSTALLER_PATH" "MeshServer")
MSH_MESHID=$(extract_msh_value "$INSTALLER_PATH" "MeshID")
MSH_SERVERID=$(extract_msh_value "$INSTALLER_PATH" "ServerID")
MSH_MESHNAME=$(extract_msh_value "$INSTALLER_PATH" "MeshName")
MSH_MESHTYPE=$(extract_msh_value "$INSTALLER_PATH" "MeshType")
MSH_INSTALLFLAGS=$(extract_msh_value "$INSTALLER_PATH" "InstallFlags")

if [[ -z "$MSH_MESHSERVER" || -z "$MSH_MESHID" || -z "$MSH_SERVERID" ]]; then
    echo -e "${RED}ERROR: no se pudo extraer el msh del binario.${NC}"
    echo "  MeshServer=$MSH_MESHSERVER"
    echo "  MeshID=$MSH_MESHID"
    echo "  ServerID=$MSH_SERVERID"
    rm -f "$INSTALLER_PATH" "$AGENT_PATH"; exit 1
fi
echo -e "${GREEN}MeshServer: $MSH_MESHSERVER${NC}"
echo -e "${GREEN}MeshName:   $MSH_MESHNAME${NC}"

# ============================================
# INSTALACION MANUAL (saltea binary -install que esta roto en Tahoe)
# ============================================
echo -e "\n${YELLOW}Limpiando instalaciones previas...${NC}"
cleanup_old_install

# Plists viejas / malformadas
sudo rm -f /Library/LaunchDaemons/meshagent.plist 2>/dev/null
sudo rm -f /Library/LaunchAgents/meshagent.plist 2>/dev/null
sudo rm -f "/Library/LaunchDaemons/${SERVICENAME}.plist" 2>/dev/null
sudo rm -f "/Library/LaunchAgents/${SERVICENAME}.plist" 2>/dev/null
# Residuos de install roto en /tmp
sudo rm -f /tmp/meshagent /tmp/meshagent.db 2>/dev/null

echo -e "${YELLOW}Creando estructura en ${INSTALL_DIR}...${NC}"
sudo mkdir -p "$INSTALL_DIR"
sudo chown root:wheel "$INSTALL_DIR"
sudo chmod 755 "$INSTALL_DIR"

# Copiar el binario RAW (sin installer JS, este es el que corre como servicio)
sudo cp "$AGENT_PATH" "$BIN"
sudo xattr -cr "$BIN"
sudo chmod 755 "$BIN"
sudo chown root:wheel "$BIN"

# Escribir .msh (varias rutas que MeshAgent puede buscar)
TMP_MSH="/tmp/agent_$$.msh"
cat > "$TMP_MSH" <<MSH_EOF
MeshName=${MSH_MESHNAME}
MeshType=${MSH_MESHTYPE}
MeshID=${MSH_MESHID}
ServerID=${MSH_SERVERID}
MeshServer=${MSH_MESHSERVER}
InstallFlags=${MSH_INSTALLFLAGS:-2}
MSH_EOF
sudo cp "$TMP_MSH" "${INSTALL_DIR}/${BIN_NAME}.msh"
sudo cp "$TMP_MSH" "${INSTALL_DIR}/${SERVICENAME}.msh"
sudo cp "$TMP_MSH" "${INSTALL_DIR}/meshagent.msh"
sudo chown root:wheel "${INSTALL_DIR}/"*.msh
sudo chmod 644 "${INSTALL_DIR}/"*.msh
rm -f "$TMP_MSH"

# Escribir plists en /tmp y mover (evita corrupcion por pipe-to-sudo)
TMP_DAEMON="/tmp/daemon_$$.plist"
cat > "$TMP_DAEMON" <<PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>${SERVICENAME}</string>
    <key>ProgramArguments</key>
    <array>
      <string>${BIN}</string>
      <string>--installedByUser=$(id -u)</string>
    </array>
    <key>WorkingDirectory</key>
    <string>${INSTALL_DIR}/</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/var/log/${SERVICENAME}.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/${SERVICENAME}.err</string>
  </dict>
</plist>
PLIST_EOF
sudo cp "$TMP_DAEMON" "/Library/LaunchDaemons/${SERVICENAME}.plist"
sudo chown root:wheel "/Library/LaunchDaemons/${SERVICENAME}.plist"
sudo chmod 644 "/Library/LaunchDaemons/${SERVICENAME}.plist"
rm -f "$TMP_DAEMON"

TMP_AGENT="/tmp/agent_$$.plist"
cat > "$TMP_AGENT" <<PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>${SERVICENAME}-launchagent</string>
    <key>ProgramArguments</key>
    <array>
      <string>${BIN}</string>
      <string>-kvmagent</string>
    </array>
    <key>LimitLoadToSessionType</key>
    <array>
      <string>Aqua</string>
      <string>LoginWindow</string>
    </array>
    <key>WorkingDirectory</key>
    <string>${INSTALL_DIR}/</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>5</integer>
  </dict>
</plist>
PLIST_EOF
sudo cp "$TMP_AGENT" "/Library/LaunchAgents/${SERVICENAME}.plist"
sudo chown root:wheel "/Library/LaunchAgents/${SERVICENAME}.plist"
sudo chmod 644 "/Library/LaunchAgents/${SERVICENAME}.plist"
rm -f "$TMP_AGENT"

# Validar plists
if ! plutil -lint "/Library/LaunchDaemons/${SERVICENAME}.plist" >/dev/null 2>&1; then
    echo -e "${RED}ERROR: plist daemon invalido.${NC}"; exit 1
fi
if ! plutil -lint "/Library/LaunchAgents/${SERVICENAME}.plist" >/dev/null 2>&1; then
    echo -e "${RED}ERROR: plist agent invalido.${NC}"; exit 1
fi

# Bootstrap
echo -e "${YELLOW}Iniciando servicios...${NC}"
sudo launchctl bootstrap system "/Library/LaunchDaemons/${SERVICENAME}.plist" 2>&1 || \
    echo -e "${YELLOW}AVISO: bootstrap del daemon devolvio error (puede estar ya cargado).${NC}"
sudo launchctl bootstrap "gui/$(id -u)" "/Library/LaunchAgents/${SERVICENAME}.plist" 2>&1 || \
    echo -e "${YELLOW}AVISO: bootstrap del agent (KVM) devolvio error.${NC}"

# Limpieza
rm -f "$INSTALLER_PATH" "$AGENT_PATH"

# Verificacion final: el agente esta corriendo?
sleep 4
if pgrep -f "${BIN_NAME}" >/dev/null 2>&1; then
    AGENT_PID=$(pgrep -f "${BIN_NAME}" | head -1)
    echo -e "\n${GREEN}========================================${NC}"
    echo -e "${GREEN}  INSTALACION EXITOSA: $clientID${NC}"
    echo -e "${GREEN}  Agente corriendo: PID $AGENT_PID${NC}"
    echo -e "${GREEN}  Path: $BIN${NC}"
    echo -e "${GREEN}========================================${NC}\n"
    echo -e "${WHITE}Verifique en MeshCentral en grupo '${MSH_MESHNAME}'${NC}\n"
    exit 0
else
    echo -e "\n${RED}========================================${NC}"
    echo -e "${RED}  ERROR: el agente no esta corriendo${NC}"
    echo -e "${RED}========================================${NC}"
    echo -e "${YELLOW}Revisar:${NC}"
    echo -e "  sudo cat /var/log/${SERVICENAME}.log"
    echo -e "  sudo cat /var/log/${SERVICENAME}.err"
    echo -e "  sudo launchctl print system/${SERVICENAME}"
    exit 1
fi
