#!/bin/bash
# ============================================================================
#  instalar-licencia.sh - Instalador OFICIAL de Astra + activacion
#
#  (c) 2026 ASTRA-ED-2026 - Distribucion y soporte. Todos los derechos reservados
#  sobre esta distribucion, el instalador y el sistema de licencias.
#  Soporte       : consulte a su proveedor
#  Referencia    : ASTRA-ED-2026
#
#  Este producto es una distribucion personalizada de Astra. El software
#  original pertenece a Cesbo OU (https://cesbo.com). Esta distribucion
#  incluye instalador, sistema de licencias, panel y soporte propios.
#
#  Un solo comando (se lo entregas al cliente con su codigo):
#
#     curl -fsSL https://SU-PROVEEDOR/instalar-licencia.sh \
#          | sudo bash -s -- --codigo ASTRA-XXXX-XXXX-XXXX
#
#  Opciones:
#     --codigo CODE   Codigo de activacion (obligatorio)
#     --puerto N      Puerto del panel (por defecto 8000)
#     --base URL      Origen del paquete (por defecto: servidor del proveedor)
#     --api URL       API de licencias (por defecto: servidor del proveedor)
#     --copia         Si hay una instalacion previa, conservarla (sin preguntar)
#     --sin-copia     Si hay una instalacion previa, NO conservarla (sin preguntar)
#
#  Instalacion:
#     * La instalacion es SIEMPRE LIMPIA: se elimina cualquier instalacion o
#       registro anterior de Astra (de cualquier origen) y se instala virgen.
#     * Si encuentra una configuracion anterior, PREGUNTA si desea conservarla:
#         SI  -> guarda una copia en el Escritorio del usuario (solo astra.conf)
#                y, al terminar, restaura la configuracion.
#         NO  -> instalacion limpia, sin copia (como siempre).
#     * La copia queda en el Escritorio del usuario y NO se borra.
#     * Si la configuracion es de un formato ANTIGUO (Lua u otro), se guarda
#       una copia igualmente (para recuperacion manual), sin restaurarla.
#     * Muestra el progreso por pasos (numeros) al copiar y al restaurar.
#     * Dentro de /etc/astra NO queda ninguna copia de seguridad.
#     * Sin pantalla (automatico) o sin respuesta en 90 s -> NO por defecto.
# ============================================================================
set -euo pipefail

# Si se corta la sesion (SSH/terminal), la instalacion NO se interrumpe.
trap '' HUP

BASE="${ASTRA_BASE:-https://astra.nexus.flashhub.net}"
API="${ASTRA_API:-https://nexus.flashhub.net/licencia}"
CODIGO=""
PUERTO="8000"
COPIAR=""                      # "", "si" o "no"
LOG="/var/log/astra-instalacion.log"

while [ $# -gt 0 ]; do
    case "$1" in
        --codigo)   CODIGO="$2"; shift 2 ;;
        --puerto)   PUERTO="$2"; shift 2 ;;
        --base)     BASE="$2"; shift 2 ;;
        --api)      API="$2"; shift 2 ;;
        --copia)    COPIAR="si"; shift ;;
        --sin-copia) COPIAR="no"; shift ;;
        *) echo "opcion desconocida: $1"; exit 1 ;;
    esac
done

[ "$(id -u)" = "0" ] || { echo "ERROR: ejecuta con sudo"; exit 1; }
[ -n "$CODIGO" ] || { echo "ERROR: falta --codigo ASTRA-XXXX-XXXX-XXXX"; exit 1; }
CODIGO="$(echo "$CODIGO" | tr '[:lower:]' '[:upper:]')"
command -v curl >/dev/null    || { echo "ERROR: falta curl"; exit 1; }
command -v python3 >/dev/null || { echo "ERROR: falta python3"; exit 1; }

# Puerto valido (1-65535)
case "$PUERTO" in
    ''|*[!0-9]*) echo "ERROR: --puerto debe ser un numero (ej: 8000)"; exit 1 ;;
esac
if [ "$PUERTO" -lt 1 ] || [ "$PUERTO" -gt 65535 ]; then
    echo "ERROR: --puerto debe estar entre 1 y 65535"
    exit 1
fi

# Registro de la instalacion (se conserva para soporte)
if command -v tee >/dev/null 2>&1; then
    exec > >(tee -a "$LOG") 2>&1
fi
echo "=== instalacion $(date '+%Y-%m-%d %H:%M:%S') ==="

# --- progreso (lineas simples con numeros) ----------------------------------
# ¿Hay consola para PREGUNTAR? (se prueba abriendo, no solo por permisos)
TTY_IN=0
if (read -r -n 0 _ </dev/tty) 2>/dev/null; then TTY_IN=1; fi

copiar_archivo() {   # <origen> <destino> <etiqueta>
    local origen="$1" destino="$2" etiqueta="${3:-Copiando}" tam copiado
    tam=$(wc -c < "$origen" 2>/dev/null || echo 0)
    echo "      $etiqueta [1/3] leyendo"
    if ! cp -a "$origen" "$destino"; then
        echo "      $etiqueta [3/3] ERROR al copiar"
        return 1
    fi
    chmod 600 "$destino" 2>/dev/null || true
    echo "      $etiqueta [2/3] copiado"
    copiado=$(wc -c < "$destino" 2>/dev/null || echo 0)
    if [ "$tam" -gt 0 ] && [ "$copiado" = "$tam" ]; then
        echo "      $etiqueta [3/3] verificado ($copiado bytes)"
        return 0
    fi
    echo "      $etiqueta [3/3] ERROR de verificacion"
    return 1
}

carpeta_copia() {   # <escritorio> -> crea e imprime una carpeta nueva (sin pisar otra)
    local base="$1/astra-copia-$(date +%Y-%m-%d_%H%M)" n=2
    while [ -e "$base" ]; do
        base="$1/astra-copia-$(date +%Y-%m-%d_%H%M)-$n"
        n=$((n + 1))
    done
    mkdir -p "$base"
    chmod 700 "$base" 2>/dev/null || true
    echo "$base"
}

echo "============================================================"
echo "  ASTRA - instalacion + activacion"
echo "  Codigo: $CODIGO     Panel: puerto $PUERTO"
echo "============================================================"

TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
cd "$TMP"

echo "[1/7] Descargando paquete de Astra..."

# --- Red robusta: reintentos + DNS alterno (DoH) + host alterno --------------
BASE_HOST="$(echo "$BASE" | sed -e 's#^[A-Za-z][A-Za-z0-9+.-]*://##' -e 's#/.*$##' -e 's#:.*$##')"
API_HOST="$(echo "$API"  | sed -e 's#^[A-Za-z][A-Za-z0-9+.-]*://##' -e 's#/.*$##' -e 's#:.*$##')"
ALT="${ASTRA_ALT:-https://nexus.flashhub.net/licencia/dist}"

resolver_doh() {
    # Resuelve un dominio por DNS-sobre-HTTPS (Cloudflare y Google) y devuelve la IP.
    local host="$1" url ip
    for url in "https://1.1.1.1/dns-query?name=$host&type=A" "https://dns.google/resolve?name=$host&type=A"; do
        ip="$(curl -4 -fsS --connect-timeout 8 --max-time 12 -H 'accept: application/dns-json' "$url" 2>/dev/null \
             | python3 -c 'import json,sys
try:
    d=json.load(sys.stdin)
    for a in d.get("Answer",[]):
        if a.get("type")==1:
            print(a.get("data","")); break
except Exception:
    pass' 2>/dev/null || true)"
        if [ -n "$ip" ]; then echo "$ip"; return 0; fi
    done
    return 1
}

descargar() {
    # 1) normal  2) DNS alterno (DoH) + IP directa  3) host alterno
    local ruta="$1" destino="$2" i ip
    for i in 1 2 3; do
        if curl -4 -fsSL --connect-timeout 20 --max-time 600 -o "$destino" "$BASE/$ruta" \
           && [ -s "$destino" ]; then return 0; fi
        sleep 2
    done
    ip="$(resolver_doh "$BASE_HOST" || true)"
    if [ -n "$ip" ]; then
        for i in 1 2 3; do
            if curl -4 -fsSL --connect-timeout 20 --max-time 600 --resolve "$BASE_HOST:443:$ip" \
               -o "$destino" "$BASE/$ruta" && [ -s "$destino" ]; then return 0; fi
            sleep 2
        done
    fi
    for i in 1 2; do
        if curl -4 -fsSL --connect-timeout 20 --max-time 600 -o "$destino" "$ALT/descargar/$ruta" \
           && [ -s "$destino" ]; then return 0; fi
        sleep 2
    done
    echo "[!] No se pudo descargar '$ruta'."
    echo "    Revisa la conexion/DNS de este equipo e intenta de nuevo."
    return 1
}

for f in astra astra.conf instalar-astra.sh desinstalar-astra.sh config-ejemplo.json \
         GUIA-DE-INSTALACION.md verificar.sh INFORME_AUDITORIA.md CAMBIOS.txt \
         CAMBIAR-PUERTO.md instalar-remoto.sh instalar-licencia.sh SHA256SUMS; do
    descargar "$f" "$f" || exit 1
done

echo "[2/7] Verificando integridad del paquete..."
if ! sha256sum -c --status SHA256SUMS; then
    echo "[!] Algun archivo llego incompleto; reintentando la descarga..."
    for f in astra astra.conf instalar-astra.sh desinstalar-astra.sh config-ejemplo.json \
             GUIA-DE-INSTALACION.md verificar.sh INFORME_AUDITORIA.md CAMBIOS.txt \
             CAMBIAR-PUERTO.md instalar-remoto.sh instalar-licencia.sh SHA256SUMS; do
        descargar "$f" "$f" || exit 1
    done
    sha256sum -c --status SHA256SUMS || { echo "[!] Paquete corrupto. Abortando."; exit 1; }
fi

echo "[3/7] Instalando Astra (limpio)..."

# --- Conservar configuracion anterior --------------------------------------
#  * Formato moderno (JSON con "make_stream"): se copia y, si el cliente dice
#    SI, se restaura automaticamente al terminar.
#  * Formato antiguo/desconocido (Lua, etc.): SIEMPRE se guarda una copia tal
#    cual (para recuperacion manual); NO se restaura automaticamente.
CONFIG_PREVIA="/etc/astra/astra.conf"
CONFIG_COPIA=""
CANALES_RESTAURADOS=""
CANALES_PREVIOS=""
CARPETA=""

localizar_configs() {
    for f in /etc/astra/astra.conf /etc/astra.conf /usr/local/etc/astra/astra.conf \
             /etc/astra/astra.lua /etc/astra/astra.conf.lua /etc/astra/astra.cfg; do
        if [ -f "$f" ]; then echo "$f"; fi
    done
}
PREVIAS="$(localizar_configs)"

if [ -n "$PREVIAS" ]; then
    MODERNA=0
    if [ -f "$CONFIG_PREVIA" ]; then
        CANALES_PREVIOS=$(python3 -c "import json;print(len(json.load(open('$CONFIG_PREVIA')).get('make_stream') or []))" 2>/dev/null || echo "")
        if [ -n "$CANALES_PREVIOS" ]; then MODERNA=1; fi
    fi

    if [ "$MODERNA" = "1" ]; then
        # ------------- configuracion MODERNA: pregunta SI/NO -------------
        RESPUESTA="$COPIAR"
        if [ -z "$RESPUESTA" ]; then
            if [ "$TTY_IN" = "1" ]; then
                echo
                echo "  Se encontro una configuracion anterior de Astra ($CANALES_PREVIOS canales)."
                printf "  Deseas conservarla y restaurarla al terminar? (s = SI / n = NO): "
                if read -r -n 1 -t 90 RESP < /dev/tty; then
                    echo
                    case "$(echo "$RESP" | tr 'A-Z' 'a-z')" in
                        s|si|sí|y|yes) RESPUESTA="si" ;;
                        *) RESPUESTA="no" ;;
                    esac
                else
                    echo
                    echo "      (sin respuesta en 90 s -> se instala LIMPIO, sin copia)"
                    RESPUESTA="no"
                fi
            else
                echo "      (instalacion automatica -> se instala LIMPIO, sin copia)"
                RESPUESTA="no"
            fi
        fi

        if [ "$RESPUESTA" = "si" ]; then
            USUARIO_COPIA="${SUDO_USER:-$(logname 2>/dev/null || echo root)}"
            HOME_USR="$(getent passwd "$USUARIO_COPIA" 2>/dev/null | cut -d: -f6 || true)"
            if [ -z "$HOME_USR" ] || [ ! -d "$HOME_USR" ]; then HOME_USR="/root"; fi
            ESCRITORIO="$HOME_USR/Escritorio"
            [ -d "$ESCRITORIO" ] || ESCRITORIO="$HOME_USR/Desktop"
            [ -d "$ESCRITORIO" ] || ESCRITORIO="$HOME_USR"
            CARPETA="$(carpeta_copia "$ESCRITORIO")"
            echo "      Conservando configuracion ($CANALES_PREVIOS canales) en:"
            echo "        $CARPETA"
            if ! copiar_archivo "$CONFIG_PREVIA" "$CARPETA/astra.conf" "Guardando copia"; then
                echo "[!] No se pudo guardar la copia. Se cancela la instalacion."
                exit 1
            fi
            cat > "$CARPETA/LEEME.txt" <<'LEEME'
COPIA DE SEGURIDAD - CONFIGURACION DE ASTRA
===========================================

Este archivo (astra.conf) es la configuracion de su servidor Astra:
canales, adaptadores, softcam, usuarios y ajustes.

Se guardo automaticamente durante una instalacion.

IMPORTANTE:
  * Contiene credenciales (softcam/usuarios). No lo comparta.
  * Para restaurarlo manualmente:
        sudo systemctl stop astra
        sudo cp astra.conf /etc/astra/astra.conf
        sudo systemctl start astra
  * Esta carpeta NO se borra sola. Puede conservarla o eliminarla usted.

Soporte: consulte a su proveedor.
LEEME
            chmod 600 "$CARPETA/LEEME.txt" 2>/dev/null || true
            if [ "$USUARIO_COPIA" != "root" ]; then
                chown -R "$USUARIO_COPIA": "$CARPETA" 2>/dev/null || true
            fi
            CANALES_COPIA=$(python3 -c "import json;print(len(json.load(open('$CARPETA/astra.conf')).get('make_stream') or []))" 2>/dev/null || echo "")
            if [ "$CANALES_COPIA" != "$CANALES_PREVIOS" ]; then
                echo "[!] No se pudo guardar la copia de forma fiable."
                echo "    Se cancela la instalacion para no perder su configuracion."
                exit 1
            fi
            CONFIG_COPIA="$CARPETA/astra.conf"
            echo "      copia verificada ($CANALES_COPIA canales)"
            for f in $PREVIAS; do
                if [ "$f" != "$CONFIG_PREVIA" ]; then
                    copiar_archivo "$f" "$CARPETA/$(basename "$f")" "Guardando $(basename "$f")" || true
                fi
            done
        else
            echo "      se instala LIMPIO (sin copia, como siempre)"
        fi
    else
        # --------- configuracion ANTIGUA/desconocida: copia SIEMPRE ---------
        USUARIO_COPIA="${SUDO_USER:-$(logname 2>/dev/null || echo root)}"
        HOME_USR="$(getent passwd "$USUARIO_COPIA" 2>/dev/null | cut -d: -f6 || true)"
        if [ -z "$HOME_USR" ] || [ ! -d "$HOME_USR" ]; then HOME_USR="/root"; fi
        ESCRITORIO="$HOME_USR/Escritorio"
        [ -d "$ESCRITORIO" ] || ESCRITORIO="$HOME_USR/Desktop"
        [ -d "$ESCRITORIO" ] || ESCRITORIO="$HOME_USR"
        CARPETA="$(carpeta_copia "$ESCRITORIO")"
        echo
        echo "  Se encontro una configuracion anterior de Astra en formato antiguo."
        echo "  Se guarda una COPIA tal cual (no se restaura automaticamente)."
        for f in $PREVIAS; do
            copiar_archivo "$f" "$CARPETA/$(basename "$f")" "Guardando $(basename "$f")" || true
        done
        cat > "$CARPETA/LEEME.txt" <<'LEEME'
COPIA DE SEGURIDAD - CONFIGURACION ANTIGUA DE ASTRA
===================================================

Esta carpeta contiene la configuracion de una instalacion anterior de
Astra en un formato ANTIGUO. Se guardo tal cual, para recuperacion
manual; NO se restauro automaticamente (el formato es distinto).

  * Contiene credenciales. No la comparta.
  * Si necesita pasar estos canales al Astra nuevo, consulte a su
    proveedor: se puede intentar una conversion del formato.
  * Esta carpeta NO se borra sola.

Soporte: consulte a su proveedor.
LEEME
        chmod 600 "$CARPETA/LEEME.txt" 2>/dev/null || true
        if [ "$USUARIO_COPIA" != "root" ]; then
            chown -R "$USUARIO_COPIA": "$CARPETA" 2>/dev/null || true
        fi
        echo "      copia guardada en: $CARPETA"
        echo "      (configuracion anterior ilegible para restaurado automatico)"
    fi
fi

# SIEMPRE LIMPIO: se elimina cualquier instalacion o registro anterior
# (de nosotros o de cualquier otro). No se conserva nada dentro del sistema.
systemctl stop astra.service astra-streams.service 2>/dev/null || true
systemctl disable astra.service astra-streams.service 2>/dev/null || true
if [ -d /etc/astra ] || [ -d /var/lib/astra ] || [ -d /var/lib/astra-guard ] || [ -e /usr/local/bin/astra ]; then
    PREVIOS=$(python3 -c "import json;print(len(json.load(open('/etc/astra/astra.conf')).get('make_stream') or []))" 2>/dev/null || echo "?")
    echo "      Instalacion previa encontrada ($PREVIOS canales) -> se elimina por completo"
fi
rm -f /usr/local/bin/astra /etc/systemd/system/astra-streams.service
rm -rf /etc/astra /var/lib/astra /var/lib/astra-guard
install -m 755 astra /usr/bin/astra

# --- Compatibilidad del ejecutable (AT_PHDR) --------------------------------
# Algunos empaquetados de Astra dejan la tabla de "program headers" del ELF
# fuera del primer segmento: en kernels antiguos (p.ej. Ubuntu 20.04 / 5.4)
# el kernel calcula mal AT_PHDR y el binario muere al arrancar (segfault).
# Aqui se detecta y se ajusta solo (idempotente; si algo no cuadra, no toca).
python3 - /usr/bin/astra <<'PY' || true
import struct, sys
p = sys.argv[1]
b = bytearray(open(p, "rb").read())

def u16(o): return struct.unpack_from("<H", b, o)[0]
def u64(o): return struct.unpack_from("<Q", b, o)[0]
def campo(i, c): return struct.unpack_from("<Q", b, e_phoff + i * 56 + c)[0]

e_phoff = u64(0x20)
e_phnum = u16(0x38)
e_phentsize = u16(0x36)
if e_phentsize != 56 or not (1 <= e_phnum <= 40):
    print("      ELF inesperado; no se toca"); sys.exit(0)

prim = None
for i in range(e_phnum):
    if struct.unpack_from("<I", b, e_phoff + i * 56)[0] == 1 and campo(i, 8) == 0:
        prim = (campo(i, 16), campo(i, 32))
        break
if not prim:
    print("      sin segmento base; no se toca"); sys.exit(0)
v0, f0 = prim

if e_phoff < f0:
    print("      ejecutable compatible (headers en posicion estandar)")
    sys.exit(0)

LEN = e_phnum * 56
DEST = 0x40
if DEST + LEN > f0:
    print("      aviso: la tabla no cabe en la zona estandar; no se toca"); sys.exit(0)

zona = bytes(b[DEST:DEST + LEN])
tabla0 = bytes(b[e_phoff:e_phoff + LEN])
es_copia = (zona[:4] == tabla0[:4]) and (zona[8:24] == tabla0[8:24])
es_ceros = all(x == 0 for x in zona)
if not (es_copia or es_ceros):
    print("      aviso: zona destino no reconocida; no se toca"); sys.exit(0)

seg = None
for i in range(e_phnum):
    base = e_phoff + i * 56
    if struct.unpack_from("<I", b, base)[0] != 1:
        continue
    off = campo(i, 8); vaddr = campo(i, 16); fsz = campo(i, 32)
    if off <= e_phoff < off + fsz:
        seg = (off, vaddr)
        break
if not seg:
    print("      aviso: tabla fuera de segmento; no se toca"); sys.exit(0)
soff, svaddr = seg

tabla = bytearray(tabla0)
nuevo_base = e_phoff + LEN + 0x98
hechos = {}
movidas = 0
for i in range(e_phnum):
    base = i * 56
    t = struct.unpack_from("<I", tabla, base)[0]
    off = struct.unpack_from("<Q", tabla, base + 8)[0]
    flen = struct.unpack_from("<Q", tabla, base + 32)[0]
    if t in (4, 0x6474e553) and flen and DEST <= off < DEST + LEN:
        if off in hechos:
            nuevo, vaddr_nuevo = hechos[off]
        else:
            nuevo = nuevo_base + movidas * 0x40
            vaddr_nuevo = svaddr + (nuevo - soff)
            if nuevo + flen > len(b) or any(b[nuevo:nuevo + flen]):
                print("      aviso: sin espacio para mover notas; no se toca"); sys.exit(0)
            b[nuevo:nuevo + flen] = b[off:off + flen]
            hechos[off] = (nuevo, vaddr_nuevo)
            movidas += 1
        struct.pack_into("<Q", tabla, base + 8, nuevo)
        struct.pack_into("<Q", tabla, base + 16, vaddr_nuevo)

b[DEST:DEST + LEN] = tabla
struct.pack_into("<Q", b, 0x20, DEST)
open(p, "wb").write(b)
print("      ejecutable ajustado (compatible con kernels antiguos y nuevos)")
PY

mkdir -p /etc/astra
[ -f /etc/astra/astra.conf ] || install -m 644 astra.conf /etc/astra/astra.conf

# Solo se bloquean los SERVIDORES DE LICENCIA (lo imprescindible).
# La ayuda oficial (help.cesbo.com -> astra.cesbo.com) queda accesible.
if ! grep -q "ls1.cesbo.com" /etc/hosts 2>/dev/null; then
    echo "127.0.0.1 ls1.cesbo.com ls2.cesbo.com ls3.cesbo.com" >> /etc/hosts
fi

echo "[4/7] Obteniendo material de activacion (codigo $CODIGO)..."
PAQUETE="$TMP/paquete.json"

pedir_paquete() {
    # 0 = ok | 2 = el servidor rechazo el codigo | 1 = sin conexion
    local i code ip
    for i in 1 2 3; do
        code="000"
        code="$(curl -4 -sS --connect-timeout 20 --max-time 120 -X POST -H "Content-Type: application/json" \
                -d "{\"codigo\":\"$CODIGO\"}" "$API/api/v1/paquete" -o "$PAQUETE" -w '%{http_code}' 2>/dev/null)" || code="000"
        case "$code" in 2*) return 0 ;; 4*) return 2 ;; esac
        sleep 2
    done
    ip="$(resolver_doh "$API_HOST" || true)"
    if [ -n "$ip" ]; then
        for i in 1 2 3; do
            code="000"
            code="$(curl -4 -sS --connect-timeout 20 --max-time 120 --resolve "$API_HOST:443:$ip" \
                    -X POST -H "Content-Type: application/json" \
                    -d "{\"codigo\":\"$CODIGO\"}" "$API/api/v1/paquete" -o "$PAQUETE" -w '%{http_code}' 2>/dev/null)" || code="000"
            case "$code" in 2*) return 0 ;; 4*) return 2 ;; esac
            sleep 2
        done
    fi
    return 1
}

RC=0
pedir_paquete || RC=$?
if [ "$RC" = "2" ]; then
    echo "[!] El servidor rechazo el codigo. Verifica con tu proveedor."
    exit 1
fi
if [ "$RC" != "0" ]; then
    echo "[!] No se pudo contactar el servidor de licencias (conexion/DNS)."
    echo "    Revisa la conexion de este equipo e intenta de nuevo."
    exit 1
fi
python3 - "$PAQUETE" <<'PY'
import base64, json, sys
d = json.load(open(sys.argv[1]))
if not d.get("ok"):
    print("ERROR:", d.get("error")); raise SystemExit(1)
open("/usr/local/share/astra-guard.py", "wb").write(base64.b64decode(d["guard_py"]))
open("/usr/local/share/astra-ed25519.py", "wb").write(base64.b64decode(d["ed25519_py"]))
json.dump(d["leasekey"], open("/etc/astra/.leasekey", "w"), indent=2)
json.dump({"cliente": d.get("cliente"), "duracion": d.get("duracion"),
           "server": d.get("server"), "sha256_binario": d.get("sha256_binario")},
          open("/etc/astra/.activacion", "w"), indent=2)
print("      cliente:", d.get("cliente"), "| duracion:", d.get("duracion"))
PY

echo "[5/7] Comprobando que el binario es el oficial..."
SHA_LOCAL=$(sha256sum /usr/bin/astra | awk '{print $1}')
SHA_OFICIAL=$(python3 -c "import json;print(json.load(open('/etc/astra/.activacion')).get('sha256_binario') or '')")
if [ -n "$SHA_OFICIAL" ] && [ "$SHA_LOCAL" != "$SHA_OFICIAL" ]; then
    echo "[!] AVISO: el binario no coincide con el hash oficial ($SHA_LOCAL vs $SHA_OFICIAL)"
    echo "    La activacion lo reportara al servidor."
else
    echo "      -> binario verificado"
fi

echo "[6/7] Instalando guardian y servicios..."
mkdir -p /usr/local/lib/astra-guard /var/lib/astra-guard /var/lib/astra
install -m 644 /usr/local/share/astra-guard.py /usr/local/lib/astra-guard/guard.py
install -m 644 /usr/local/share/astra-ed25519.py /usr/local/lib/astra-guard/ed25519.py
chmod 600 /etc/astra/.leasekey
cat > /usr/bin/astra-guard <<'EOF'
#!/bin/sh
exec /usr/bin/python3 /usr/local/lib/astra-guard/guard.py "$@"
EOF
chmod 755 /usr/bin/astra-guard

python3 - "$API" "$CODIGO" <<'PY'
import json, sys
api, codigo = sys.argv[1], sys.argv[2]
cfg = {
    "server": api,
    "lease_path": "/etc/astra/.lease",
    "license_path": "/etc/astra/.lic",
    "pubkey_path": "/etc/astra/.server.pub",
    "secret_path": "/etc/astra/.leasekey",
    "state_path": "/var/lib/astra-guard/state.json",
    "binario_path": "/usr/bin/astra",
    "check_interval_min": 720,
    "http_timeout": 20,
    "insecure_tls": False,
    "auto_start_astra": True,
    "codigo": codigo,
}
json.dump(cfg, open("/etc/astra/guard.json", "w"), indent=2)
PY
chmod 600 /etc/astra/guard.json

cat > /etc/systemd/system/astra-guard.service <<'EOF'
[Unit]
Description=Astra Guard (activacion / vigencia)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/astra-guard -c /etc/astra/guard.json daemon
Restart=always
RestartSec=15
User=root

[Install]
WantedBy=multi-user.target
EOF

cat > /etc/systemd/system/astra.service <<EOF
[Unit]
Description=Astra Media Server
After=network.target astra-guard.service
Requires=astra-guard.service
ConditionPathExists=/etc/astra/.lease

[Service]
Type=simple
ExecStartPre=/usr/bin/astra-guard check
ExecStartPre=/bin/sleep 8
ExecStart=/usr/bin/astra -c /etc/astra/astra.conf -p $PUERTO
Restart=always
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable astra-guard.service >/dev/null 2>&1 || true
systemctl enable astra.service >/dev/null 2>&1 || true

# Puerto: aviso si esta ocupado + apertura en firewall (si hay)
if command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]$PUERTO$"; then
    echo "      AVISO: el puerto $PUERTO ya esta ocupado por otro programa"
fi
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "^Status: active"; then
    ufw allow "$PUERTO"/tcp >/dev/null 2>&1 && echo "      firewall (ufw): puerto $PUERTO/tcp abierto"
fi
if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
    firewall-cmd --permanent --add-port="$PUERTO"/tcp >/dev/null 2>&1 && firewall-cmd --reload >/dev/null 2>&1 && echo "      firewall (firewalld): puerto $PUERTO/tcp abierto"
fi

systemctl restart astra-guard.service

echo "[7/7] Vinculando el equipo..."
for i in $(seq 1 10); do
    sleep 3
    if /usr/bin/astra-guard -c /etc/astra/guard.json check >/dev/null 2>&1; then break; fi
    /usr/bin/astra-guard -c /etc/astra/guard.json enroll >/dev/null 2>&1 || true
done

if /usr/bin/astra-guard -c /etc/astra/guard.json check >/dev/null 2>&1; then
    systemctl restart astra.service
    sleep 3

    # --- Restaurar la configuracion anterior (si el cliente dijo SI) --------
    if [ -n "$CONFIG_COPIA" ] && [ -f "$CONFIG_COPIA" ]; then
        echo
        echo "Restaurando su configuracion anterior..."
        echo "      [1/6] deteniendo Astra"
        systemctl stop astra.service 2>/dev/null || true
        cp -a /etc/astra/astra.conf "$TMP/astra.conf.limpio" 2>/dev/null || true
        echo "      [2/6] copiando la configuracion guardada"
        if install -m 644 "$CONFIG_COPIA" /etc/astra/astra.conf; then
            echo "      [3/6] limpiando restos de copias"
            find /etc/astra -maxdepth 1 \( -name '*.backup' -o -name '*.bak*' -o -name '*.licencia-ok' -o -name '*~' \) -delete 2>/dev/null || true
            echo "      [4/6] renovando el permiso"
            /usr/bin/astra-guard -c /etc/astra/guard.json heartbeat >/dev/null 2>&1 || true
            echo "      [5/6] arrancando Astra"
            systemctl start astra.service 2>/dev/null || true
            i=0
            while [ "$i" -lt 30 ]; do
                if systemctl is-active astra.service >/dev/null 2>&1; then break; fi
                i=$((i + 1))
                if [ $((i % 5)) = 0 ]; then echo "            ... esperando arranque (${i}s)"; fi
                sleep 1
            done
            echo "      [6/6] verificando canales"
            CANALES_RESTAURADOS=$(python3 -c "import json;print(len(json.load(open('/etc/astra/astra.conf')).get('make_stream') or []))" 2>/dev/null || echo "0")
            if ! systemctl is-active astra.service >/dev/null 2>&1 || [ "$CANALES_RESTAURADOS" = "0" ]; then
                # Rollback seguro: se deja la instalacion limpia funcionando
                install -m 644 "$TMP/astra.conf.limpio" /etc/astra/astra.conf 2>/dev/null || true
                systemctl restart astra.service 2>/dev/null || true
                CANALES_RESTAURADOS=""
                echo "      AVISO: no se pudo restaurar la configuracion."
                echo "      Se dejo la instalacion limpia funcionando. Su copia sigue a salvo."
            else
                echo "      configuracion restaurada ($CANALES_RESTAURADOS canales)"
            fi
        else
            echo "      AVISO: no se pudo copiar la configuracion; se deja el Astra limpio."
        fi
    fi

    echo
    echo "============================================================"
    echo "  INSTALADO Y ACTIVADO"
    echo "============================================================"
    /usr/bin/astra-guard -c /etc/astra/guard.json status | grep -v -i "servidor"
    IP=$(hostname -I 2>/dev/null | awk '{print $1}')
    echo
    if [ -n "$CONFIG_COPIA" ]; then
        if [ -n "$CANALES_RESTAURADOS" ]; then
            echo "  Configuracion: RESTAURADA ($CANALES_RESTAURADOS canales)"
        else
            echo "  Configuracion: instalacion limpia (copia no restaurada)"
        fi
        echo "  Copia segura : $CONFIG_COPIA"
        echo "                 (no se borra; puede eliminarla usted mismo)"
    fi
    echo "  Panel Astra : http://$IP:$PUERTO   (admin / admin)"
    echo "  Vigencia    : en el panel -> Settings -> License"
    [ "$PUERTO" = "8000" ] || echo "  Puerto      : $PUERTO (recuerda abrirlo en tu router/firewall si quieres acceso externo)"
    echo "------------------------------------------------------------"
    echo "  Distribucion: edicion licenciada (ref. ASTRA-ED-2026)"
    echo "  Soporte    : consulte a su proveedor"
    echo "============================================================"
else
    echo
    echo "AVISO: el equipo no pudo vincularse todavia."
    echo "Revisa: journalctl -u astra-guard -n 40"
    echo "Si el codigo ya fue usado en OTRO equipo, pide uno nuevo."
    [ -n "$CONFIG_COPIA" ] && echo "Su copia quedo a salvo en: $CONFIG_COPIA"
fi
