#!/usr/bin/env bash
# wg_hub_setup — Configura el host como hub WireGuard (servidor central).
# Crea /etc/wireguard/wg0.conf con [Interface] block, abre UDP en firewall,
# habilita ip_forward persistente, arranca y verifica wg-quick@wg0.
# Idempotente: si el conf existe con la misma PrivateKey -> no-op.
# Emite JSON a stdout. Logs a stderr con prefijo [wg_hub_setup].
# Exit 0 = éxito, 1 = fallo.

wg_hub_setup() {
    local private_key="${1:-}"
    local subnet_cidr="${2:-10.42.0.1/24}"
    local listen_port="${3:-51820}"

    _wg_hub_log() { echo "[wg_hub_setup] $*" >&2; }

    # ── Validación de entradas ──────────────────────────────────────────────

    # private_key: base64 estándar de 44 caracteres (32 bytes)
    if [[ -z "${private_key}" ]]; then
        _wg_hub_log "ERROR: private_key requerida (base64 44 chars, generada por wg genkey)"
        return 1
    fi
    if ! [[ "${private_key}" =~ ^[A-Za-z0-9+/]{43}=$ ]]; then
        _wg_hub_log "ERROR: private_key no parece base64 válida (se esperan 44 chars terminando en '=')"
        return 1
    fi

    # subnet_cidr: 10.x.x.x/nn
    if ! [[ "${subnet_cidr}" =~ ^10\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]]; then
        _wg_hub_log "ERROR: subnet_cidr debe ser 10.x.x.x/nn, recibido: '${subnet_cidr}'"
        return 1
    fi

    # listen_port: 1024-65535
    if ! [[ "${listen_port}" =~ ^[0-9]+$ ]] || (( listen_port < 1024 || listen_port > 65535 )); then
        _wg_hub_log "ERROR: listen_port debe ser un entero entre 1024 y 65535, recibido: '${listen_port}'"
        return 1
    fi

    # ── Verificar que wireguard-tools esté instalado ────────────────────────
    if ! command -v wg &>/dev/null; then
        _wg_hub_log "ERROR: 'wg' no encontrado. Ejecuta wg_install primero."
        return 1
    fi

    if ! command -v wg-quick &>/dev/null; then
        _wg_hub_log "ERROR: 'wg-quick' no encontrado. Instala wireguard-tools."
        return 1
    fi

    # ── Extraer hub_ip (parte sin CIDR prefix) y determinar config_path ────
    local hub_ip="${subnet_cidr%%/*}"
    local config_path="/etc/wireguard/wg0.conf"
    local interface="wg0"
    local action_status=""

    # ── Idempotencia: comparar PrivateKey existente ─────────────────────────
    if [[ -f "${config_path}" ]]; then
        local existing_key
        existing_key=$(sudo grep -E '^\s*PrivateKey\s*=' "${config_path}" 2>/dev/null \
                       | head -n1 | sed 's/.*=\s*//')
        if [[ "${existing_key}" == "${private_key}" ]]; then
            _wg_hub_log "Config existente con misma PrivateKey — no-op (status=already-configured)"
            printf '{"status":"already-configured","config_path":"%s","interface":"%s","hub_ip":"%s"}\n' \
                "${config_path}" "${interface}" "${hub_ip}"
            return 0
        else
            _wg_hub_log "Config existente con PrivateKey DIFERENTE — haciendo backup y reescribiendo"
            local backup_path="${config_path}.bak.$(date +%Y%m%d%H%M%S)"
            sudo cp "${config_path}" "${backup_path}" \
                || { _wg_hub_log "ERROR: no se pudo hacer backup en ${backup_path}"; return 1; }
            _wg_hub_log "Backup guardado en ${backup_path}"
            action_status="reconfigured"
        fi
    else
        action_status="configured"
    fi

    # ── Asegurar que /etc/wireguard existe con permisos correctos ───────────
    if [[ ! -d /etc/wireguard ]]; then
        sudo mkdir -p /etc/wireguard \
            || { _wg_hub_log "ERROR: no se pudo crear /etc/wireguard"; return 1; }
        sudo chmod 700 /etc/wireguard
        _wg_hub_log "Directorio /etc/wireguard creado"
    fi

    # ── Escribir /etc/wireguard/wg0.conf ────────────────────────────────────
    _wg_hub_log "Escribiendo ${config_path} (Address=${subnet_cidr}, ListenPort=${listen_port})"
    sudo tee "${config_path}" > /dev/null <<EOF
[Interface]
Address = ${subnet_cidr}
ListenPort = ${listen_port}
PrivateKey = ${private_key}
SaveConfig = false

# NAT: permite que los peers accedan a internet via este hub (opcional, comentar si no se desea)
PostUp   = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
EOF

    if [[ $? -ne 0 ]]; then
        _wg_hub_log "ERROR: no se pudo escribir ${config_path}"
        return 1
    fi

    sudo chmod 600 "${config_path}" \
        || { _wg_hub_log "ERROR: chmod 600 ${config_path} falló"; return 1; }
    _wg_hub_log "Permisos 600 aplicados a ${config_path}"

    # ── Habilitar ip_forward persistente ────────────────────────────────────
    local sysctl_file="/etc/sysctl.d/99-wireguard.conf"
    if [[ ! -f "${sysctl_file}" ]] || ! grep -q "net.ipv4.ip_forward" "${sysctl_file}" 2>/dev/null; then
        _wg_hub_log "Habilitando ip_forward en ${sysctl_file}"
        echo "net.ipv4.ip_forward = 1" | sudo tee "${sysctl_file}" > /dev/null \
            || { _wg_hub_log "ERROR: no se pudo escribir ${sysctl_file}"; return 1; }
    fi
    sudo sysctl -p "${sysctl_file}" >&2 \
        || _wg_hub_log "WARN: sysctl -p falló (puede ignorarse si el kernel ya tiene ip_forward=1)"

    # ── Abrir puerto en firewall ─────────────────────────────────────────────
    if command -v ufw &>/dev/null && sudo ufw status 2>/dev/null | grep -q "Status: active"; then
        _wg_hub_log "ufw activo — abriendo UDP/${listen_port}"
        sudo ufw allow "${listen_port}/udp" >&2 \
            || _wg_hub_log "WARN: ufw allow ${listen_port}/udp falló (verificar manualmente)"
    elif command -v iptables &>/dev/null; then
        _wg_hub_log "ufw inactivo — usando iptables para abrir UDP/${listen_port}"
        sudo iptables -C INPUT -p udp --dport "${listen_port}" -j ACCEPT 2>/dev/null \
            || sudo iptables -A INPUT -p udp --dport "${listen_port}" -j ACCEPT >&2 \
            || _wg_hub_log "WARN: iptables INPUT rule falló (verificar manualmente)"
    else
        _wg_hub_log "WARN: ni ufw ni iptables disponibles — abre el puerto ${listen_port}/udp manualmente"
    fi

    # ── Detener interfaz si estaba corriendo (para aplicar nueva config) ────
    if sudo wg show "${interface}" &>/dev/null 2>&1; then
        _wg_hub_log "Interfaz ${interface} activa — deteniendo antes de reconfigurar"
        sudo systemctl stop "wg-quick@${interface}" 2>/dev/null \
            || sudo wg-quick down "${interface}" 2>/dev/null \
            || _wg_hub_log "WARN: no se pudo detener ${interface} (puede que no estuviera activa)"
    fi

    # ── Habilitar y arrancar wg-quick@wg0 ────────────────────────────────────
    _wg_hub_log "Habilitando systemd unit wg-quick@${interface}"
    sudo systemctl enable "wg-quick@${interface}" >&2 \
        || { _wg_hub_log "ERROR: systemctl enable wg-quick@${interface} falló"; return 1; }

    _wg_hub_log "Arrancando wg-quick@${interface}"
    sudo systemctl start "wg-quick@${interface}" >&2 \
        || { _wg_hub_log "ERROR: systemctl start wg-quick@${interface} falló"; return 1; }

    # ── Verificar que la interfaz está UP ────────────────────────────────────
    local retries=5
    local up=0
    for (( i=0; i<retries; i++ )); do
        if sudo wg show "${interface}" &>/dev/null 2>&1; then
            up=1
            break
        fi
        sleep 1
    done

    if [[ "${up}" -eq 0 ]]; then
        _wg_hub_log "ERROR: 'wg show ${interface}' falló tras ${retries}s — la interfaz no arrancó"
        return 1
    fi

    _wg_hub_log "Interfaz ${interface} UP (status=${action_status})"
    printf '{"status":"%s","config_path":"%s","interface":"%s","hub_ip":"%s"}\n' \
        "${action_status}" "${config_path}" "${interface}" "${hub_ip}"
    return 0
}
