#!/usr/bin/env bash
# ==============================================================================
# NexusPBX HA - Script Helper Root pour l'orchestration Keepalived
# ==============================================================================
set -euo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"

CMD="${1:-status}"

case "$CMD" in
    apply)
        # 1. Activation kernel sysctl pour liaison IP non-locale (indispensable HA)
        sysctl -w net.ipv4.ip_nonlocal_bind=1 >/dev/null 2>&1 || true
        if [ ! -f /etc/sysctl.d/99-nexus-ha.conf ]; then
            echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/99-nexus-ha.conf 2>/dev/null || true
        fi

        # 2. Vérification et installation éventuelle de Keepalived et arping
        if ! command -v keepalived >/dev/null 2>&1 || ! command -v arping >/dev/null 2>&1; then
            apt-get update -qq && apt-get install -y -qq keepalived iputils-arping >/dev/null 2>&1 || true
        fi

        # 3. Création et sécurisation stricte du dossier des scripts Keepalived (exigences strictes Keepalived 2.x)
        mkdir -p /etc/keepalived/scripts
        if [ -d "/var/www/nexus-pbx/scripts/cluster" ]; then
            cp -f /var/www/nexus-pbx/scripts/cluster/*.sh /etc/keepalived/scripts/ 2>/dev/null || true
        fi
        chown -R root:root /etc/keepalived
        chmod 755 /etc/keepalived
        chmod 755 /etc/keepalived/scripts
        chmod 755 /etc/keepalived/scripts/*.sh 2>/dev/null || true

        # 4. Déploiement de la configuration générée depuis /tmp
        if [ -f "/tmp/keepalived.conf.tmp" ]; then
            cp -f /tmp/keepalived.conf.tmp /etc/keepalived/keepalived.conf
            chown root:root /etc/keepalived/keepalived.conf
            chmod 644 /etc/keepalived/keepalived.conf
            rm -f /tmp/keepalived.conf.tmp
        fi

        # Fichier journal du cluster accessible en écriture
        touch /var/log/nexus_cluster.log
        chmod 666 /var/log/nexus_cluster.log

        # 5. Activation et démarrage du service
        systemctl daemon-reload
        systemctl unmask keepalived >/dev/null 2>&1 || true
        systemctl enable keepalived >/dev/null 2>&1 || true
        systemctl restart keepalived
        echo "KEEPALIVED_APPLIED_OK"
        ;;

    stop)
        systemctl stop keepalived >/dev/null 2>&1 || true
        systemctl disable keepalived >/dev/null 2>&1 || true
        iptables -D INPUT -p udp -m multiport --dports 5060,5061 -j REJECT --reject-with icmp-port-unreachable 2>/dev/null || true
        iptables -D INPUT -p tcp -m multiport --dports 5060,5061 -j REJECT --reject-with tcp-reset 2>/dev/null || true
        echo "KEEPALIVED_STOPPED_OK"
        ;;

    status)
        if systemctl is-active --quiet keepalived 2>/dev/null; then
            echo "RUNNING"
        else
            echo "STOPPED"
        fi
        ;;

    restore-astdb)
        SRC="${2:-/tmp/astdb_cluster_sync.sqlite3}"
        DEST="/var/lib/asterisk/astdb.sqlite3"
        if [ ! -f "$SRC" ] || [ ! -s "$SRC" ]; then
            echo "ASTDB_RESTORE_NO_FILE"
            exit 0
        fi

        # 1. Vérification intégrité du fichier source temporaire
        SRC_INTEGRITY=$(sqlite3 "$SRC" "PRAGMA integrity_check;" 2>/dev/null || echo "corrupt")
        if [ "$SRC_INTEGRITY" != "ok" ]; then
            echo "ASTDB_SOURCE_CORRUPT: $SRC_INTEGRITY"
            rm -f "$SRC"
            exit 1
        fi

        chmod 644 "$SRC" 2>/dev/null || true

        # 2. Si Asterisk tourne, injection via son moteur interne database query
        # Cela garantit l'absence totale de verrou SQLite (database is locked) et préserve les descripteurs ouverts
        # IMPORTANT : Ne jamais répliquer /subscription_persistence/ car les dialogues SIP BLF sont des objets en RAM
        # propres à l'instance Asterisk d'origine. Les purger force le nouveau Master à envoyer 481, ce qui incite
        # immédiatement les téléphones (Yealink T46S, T54W) à réémettre un dialogue BLF propre en 25ms.
        if pidof asterisk >/dev/null 2>&1; then
            /usr/sbin/asterisk -rx "database query \"ATTACH '$SRC' AS src;\"" >/dev/null 2>&1 || true
            /usr/sbin/asterisk -rx "database query \"DELETE FROM astdb WHERE key LIKE '/registrar/%';\"" >/dev/null 2>&1 || true
            /usr/sbin/asterisk -rx "database query \"INSERT INTO astdb SELECT * FROM src.astdb WHERE key LIKE '/registrar/%';\"" >/dev/null 2>&1 || true
            /usr/sbin/asterisk -rx "database query \"INSERT OR REPLACE INTO astdb SELECT * FROM src.astdb WHERE key NOT LIKE '/registrar/%' AND key NOT LIKE '/subscription_persistence/%';\"" >/dev/null 2>&1 || true
            /usr/sbin/asterisk -rx "database query \"DETACH src;\"" >/dev/null 2>&1 || true
            /usr/sbin/asterisk -rx "database deltree subscription_persistence" >/dev/null 2>&1 || true
        else
            # Asterisk arrêté : copie directe intègre en purgeant les souscriptions zombies
            cp -f "$SRC" "$DEST"
            sqlite3 "$DEST" "DELETE FROM astdb WHERE key LIKE '/subscription_persistence/%';" 2>/dev/null || true
            chown asterisk:asterisk "$DEST" 2>/dev/null || true
            chmod 664 "$DEST" 2>/dev/null || true
        fi

        rm -f "$SRC"
        echo "ASTDB_RESTORED_OK"
        ;;

    fix-astdb)
        DEST="/var/lib/asterisk/astdb.sqlite3"
        if [ -f "$DEST" ]; then
            INTEGRITY=$(sqlite3 "$DEST" "PRAGMA integrity_check;" 2>/dev/null || echo "corrupt")
            if [ "$INTEGRITY" != "ok" ]; then
                mv -f "$DEST" "${DEST}.corrupt.$(date +%s)" 2>/dev/null || true
                sqlite3 "$DEST" "CREATE TABLE astdb(key TEXT, value TEXT, PRIMARY KEY(key));" 2>/dev/null || true
                chown asterisk:asterisk "$DEST" 2>/dev/null || true
                chmod 664 "$DEST" 2>/dev/null || true
                if pidof asterisk >/dev/null 2>&1; then
                    /usr/sbin/asterisk -rx "pjsip reload" 2>/dev/null || true
                fi
                echo "ASTDB_FIXED"
                exit 0
            fi
        fi
        echo "ASTDB_OK"
        ;;

    check)
        if pidof asterisk >/dev/null 2>&1 && /usr/sbin/asterisk -rx "core show uptime" 2>/dev/null | grep -qi "uptime"; then
            echo "HEALTHY"
        else
            echo "DEGRADED"
        fi
        ;;

    *)
        echo "Usage: $0 {apply|stop|status|check}"
        exit 1
        ;;
esac
