#!/bin/sh
#
# RMM Agent Plugin Installer fuer OPNsense
#
# Usage (automatischer Download):
#   fetch -o /tmp/install.sh "https://git.cynfo.net/christian/rmm/raw/branch/main/opnsense-plugin/install.sh"
#   sh /tmp/install.sh
#
# Oder mit lokalen Binaries:
#   scp install.sh rmm-agent rmm-updater-freebsd root@opnsense:/tmp/
#   ssh root@opnsense '/bin/sh /tmp/install.sh'
#

set -e

BASEDIR="/tmp"
BINARY="${BASEDIR}/rmm-agent"
UPDATER_BINARY="${BASEDIR}/rmm-updater"
BACKEND_URL="${RMM_BACKEND:-https://your-backend:8443}"
API_KEY="${RMM_API_KEY:-YOUR_API_KEY}"

echo "========================================"
echo " RMM Agent Plugin Installer"
echo "========================================"

# Binaries herunterladen wenn nicht lokal vorhanden
if [ ! -f "${BINARY}" ]; then
    echo "[*] Binaries nicht lokal gefunden — lade vom Backend..."
    /usr/bin/fetch --no-verify-peer --no-verify-hostname -o "${BASEDIR}/rmm-bundle.zip" \
        "${BACKEND_URL}/api/v1/firmware/download?platform=freebsd&api_key=${API_KEY}"
    if [ ! -f "${BASEDIR}/rmm-bundle.zip" ]; then
        echo "FEHLER: Download fehlgeschlagen."
        echo "Pruefe Backend-Erreichbarkeit: ${BACKEND_URL}"
        exit 1
    fi
    cd "${BASEDIR}"
    unzip -o rmm-bundle.zip 2>/dev/null || true
    # ZIP enthaelt rmm-agent und rmm-updater-freebsd
    if [ -f "${BASEDIR}/rmm-updater-freebsd" ] && [ ! -f "${UPDATER_BINARY}" ]; then
        mv "${BASEDIR}/rmm-updater-freebsd" "${UPDATER_BINARY}"
    fi
    rm -f rmm-bundle.zip
    echo "  Download OK"
    if [ ! -f "${BINARY}" ]; then
        echo "FEHLER: ${BINARY} nach Download nicht vorhanden."
        echo "Firmware-ZIP muss 'rmm-agent' enthalten."
        exit 1
    fi
fi

# Alten Agent stoppen falls vorhanden
echo "[1/8] Alte Dienste stoppen..."
/usr/local/etc/rc.d/rmm_updater stop 2>/dev/null || true
/usr/local/etc/rc.d/rmm_agent stop 2>/dev/null || true
pkill -9 -f rmm-agent 2>/dev/null || true
pkill -9 -f rmm-updater 2>/dev/null || true

# Binaries installieren
echo "[2/8] Binaries installieren..."
mkdir -p /usr/local/rmm
cp "${BINARY}" /usr/local/rmm/rmm-agent
chmod +x /usr/local/rmm/rmm-agent
if [ -f "${UPDATER_BINARY}" ]; then
    cp "${UPDATER_BINARY}" /usr/local/rmm/rmm-updater
    chmod +x /usr/local/rmm/rmm-updater
    echo "  Updater installiert"
else
    echo "  Updater-Binary nicht gefunden — uebersprungen"
fi

# rc.d Script
echo "[3/8] rc.d Service installieren..."
cat > /usr/local/etc/rc.d/rmm_agent << 'RCEOF'
#!/bin/sh
#
# PROVIDE: rmm_agent
# REQUIRE: NETWORKING
# KEYWORD: shutdown

. /etc/rc.subr

name="rmm_agent"
rcvar="rmm_agent_enable"

load_rc_config ${name}

: ${rmm_agent_enable:="NO"}
: ${rmm_agent_config:="/usr/local/rmm/config.yaml"}
: ${rmm_agent_logfile:="/var/log/rmm-agent.log"}
: ${rmm_agent_insecure:="YES"}

pidfile="/var/run/${name}.pid"
command="/usr/local/rmm/rmm-agent"

start_cmd="${name}_start"
stop_cmd="${name}_stop"
status_cmd="${name}_status"

rmm_agent_start()
{
    if [ ! -f "${command}" ]; then
        echo "rmm-agent binary not found: ${command}"
        return 1
    fi
    if [ ! -f "${rmm_agent_config}" ]; then
        echo "Config not found: ${rmm_agent_config}"
        return 1
    fi
    echo "Starting ${name}."
    INSECURE_FLAG=""
    if checkyesno rmm_agent_insecure; then
        INSECURE_FLAG="--insecure"
    fi
    /usr/sbin/daemon -f -p ${pidfile} -o ${rmm_agent_logfile} \
        ${command} --config ${rmm_agent_config} ${INSECURE_FLAG}
}

rmm_agent_stop()
{
    if [ -f "${pidfile}" ]; then
        echo "Stopping ${name}."
        kill $(cat ${pidfile}) 2>/dev/null
        rm -f ${pidfile}
    else
        echo "${name} is not running."
    fi
}

rmm_agent_status()
{
    if [ -f "${pidfile}" ] && kill -0 $(cat ${pidfile}) 2>/dev/null; then
        echo "${name} is running as pid $(cat ${pidfile})."
    else
        echo "${name} is not running."
        return 1
    fi
}

run_rc_command "$1"
RCEOF
chmod +x /usr/local/etc/rc.d/rmm_agent

# Updater rc.d Service
cat > /usr/local/etc/rc.d/rmm_updater << 'RCEOF2'
#!/bin/sh
#
# PROVIDE: rmm_updater
# REQUIRE: NETWORKING rmm_agent
# KEYWORD: shutdown

. /etc/rc.subr

name="rmm_updater"
rcvar="${name}_enable"
command="/usr/local/rmm/rmm-updater"
pidfile="/var/run/${name}.pid"
logfile="/var/log/rmm-updater.log"

start_cmd="${name}_start"
stop_cmd="${name}_stop"
status_cmd="${name}_status"

rmm_updater_start()
{
    if [ ! -f "${command}" ]; then
        echo "rmm-updater binary not found: ${command}"
        return 1
    fi
    if [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null; then
        echo "${name} already running"
        return 0
    fi
    echo "Starting ${name}."
    /usr/sbin/daemon -f -p "$pidfile" -o "$logfile" "$command"
}

rmm_updater_stop()
{
    if [ -f "${pidfile}" ]; then
        echo "Stopping ${name}."
        kill $(cat ${pidfile}) 2>/dev/null
        rm -f ${pidfile}
    else
        echo "${name} is not running."
    fi
}

rmm_updater_status()
{
    if [ -f "${pidfile}" ] && kill -0 $(cat ${pidfile}) 2>/dev/null; then
        echo "${name} is running as pid $(cat ${pidfile})."
    else
        echo "${name} is not running."
        return 1
    fi
}

load_rc_config $name
: ${rmm_updater_enable:="NO"}
run_rc_command "$1"
RCEOF2
chmod +x /usr/local/etc/rc.d/rmm_updater

# MVC Plugin installieren
echo "[4/8] OPNsense MVC Plugin installieren..."

# Model
mkdir -p /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/Menu
cat > /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/RMMAgent.xml << 'XMLEOF'
<model>
    <mount>//OPNsense/rmmagent</mount>
    <version>1.0.0</version>
    <description>RMM Agent Settings</description>
    <items>
        <general>
            <Enabled type="BooleanField">
                <Default>0</Default>
                <Required>Y</Required>
            </Enabled>
            <BackendUrl type="TextField">
                <Required>Y</Required>
                <Mask>/^https?:\/\/.+/</Mask>
                <ValidationMessage>Gueltige URL erforderlich (https://host:port)</ValidationMessage>
            </BackendUrl>
            <ApiKey type="TextField">
                <Required>Y</Required>
                <Mask>/^.{8,}$/</Mask>
                <ValidationMessage>API-Key muss mindestens 8 Zeichen lang sein</ValidationMessage>
            </ApiKey>
            <AgentName type="TextField">
                <Required>N</Required>
            </AgentName>
            <Interval type="IntegerField">
                <Default>60</Default>
                <MinimumValue>10</MinimumValue>
                <MaximumValue>3600</MaximumValue>
                <ValidationMessage>Intervall zwischen 10 und 3600 Sekunden</ValidationMessage>
            </Interval>
            <Insecure type="BooleanField">
                <Default>1</Default>
                <Required>Y</Required>
            </Insecure>
        </general>
    </items>
</model>
XMLEOF

cat > /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/RMMAgent.php << 'PHPEOF'
<?php
namespace OPNsense\RMMAgent;
use OPNsense\Base\BaseModel;
class RMMAgent extends BaseModel
{
}
PHPEOF

cat > /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/Menu/Menu.xml << 'MENUEOF'
<menu>
    <Services>
        <RMMAgent VisibleName="RMM Agent" cssClass="fa fa-shield" url="/ui/rmmagent/general" order="99">
        </RMMAgent>
    </Services>
</menu>
MENUEOF

# Controllers
mkdir -p /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api
mkdir -p /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/forms

cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/GeneralController.php << 'PHPEOF'
<?php
namespace OPNsense\RMMAgent;
use OPNsense\Base\IndexController;
class GeneralController extends IndexController
{
    public function indexAction()
    {
        $this->view->generalForm = $this->getForm('general');
        $this->view->pick('OPNsense/RMMAgent/general');
    }
}
PHPEOF

cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/SettingsController.php << 'PHPEOF'
<?php
namespace OPNsense\RMMAgent\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\RMMAgent\RMMAgent;
use OPNsense\Core\Config;

class SettingsController extends ApiControllerBase
{
    public function getAction()
    {
        $mdl = new RMMAgent();
        $result = array('rmmagent' => array('general' => array()));
        foreach ($mdl->general->iterateItems() as $key => $node) {
            $result['rmmagent']['general'][$key] = (string)$node;
        }
        return $result;
    }

    public function setAction()
    {
        $result = array('result' => 'failed');
        if ($this->request->isPost()) {
            $mdl = new RMMAgent();
            $post = null;

            // OPNsense sends data as rmmagent[general][KEY]
            $rmmagent = $this->request->getPost('rmmagent');
            if (!empty($rmmagent) && isset($rmmagent['general'])) {
                $post = $rmmagent['general'];
            }

            // Fallback: JSON body
            if (empty($post)) {
                $rawBody = $this->request->getRawBody();
                if (!empty($rawBody)) {
                    $jsonData = json_decode($rawBody, true);
                    if (isset($jsonData['rmmagent']['general'])) {
                        $post = $jsonData['rmmagent']['general'];
                    } elseif (isset($jsonData['general'])) {
                        $post = $jsonData['general'];
                    }
                }
            }

            if (!empty($post)) {
                foreach ($post as $key => $value) {
                    try {
                        $node = $mdl->general->$key;
                        if ($node !== null) {
                            $node->setValue($value);
                        }
                    } catch (\Exception $e) {
                        // skip
                    }
                }
            }

            $valMsgs = $mdl->performValidation();
            if ($valMsgs->count() > 0) {
                $result['validations'] = array();
                foreach ($valMsgs as $msg) {
                    $result['validations']['rmmagent.general.' . $msg->getField()] = $msg->getMessage();
                }
            } else {
                $mdl->serializeToConfig();
                Config::getInstance()->save();
                $result['result'] = 'saved';
            }
        }
        return $result;
    }
}
PHPEOF

cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/ServiceController.php << 'PHPEOF'
<?php
namespace OPNsense\RMMAgent\Api;
use OPNsense\Base\ApiControllerBase;

class ServiceController extends ApiControllerBase
{
    public function statusAction()
    {
        $backend = new \OPNsense\Core\Backend();
        $response = trim($backend->configdRun('rmmagent status'));
        if (strpos($response, 'is running') !== false) {
            return array('status' => 'running', 'message' => $response);
        }
        return array('status' => 'stopped', 'message' => $response);
    }

    public function startAction()
    {
        $backend = new \OPNsense\Core\Backend();
        return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent start')));
    }

    public function stopAction()
    {
        $backend = new \OPNsense\Core\Backend();
        return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent stop')));
    }

    public function restartAction()
    {
        $backend = new \OPNsense\Core\Backend();
        return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent restart')));
    }

    public function reconfigureAction()
    {
        $backend = new \OPNsense\Core\Backend();
        return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent reconfigure')));
    }
}
PHPEOF

# Forms
cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/forms/general.xml << 'FORMEOF'
<form>
    <field>
        <id>rmmagent.general.Enabled</id>
        <label>Aktiviert</label>
        <type>checkbox</type>
        <help>RMM Agent Dienst aktivieren/deaktivieren</help>
    </field>
    <field>
        <id>rmmagent.general.BackendUrl</id>
        <label>Backend URL</label>
        <type>text</type>
        <help>URL des RMM Backends (z.B. https://your-backend:8443)</help>
    </field>
    <field>
        <id>rmmagent.general.ApiKey</id>
        <label>API Key</label>
        <type>password</type>
        <help>API-Schluessel fuer die Authentifizierung am Backend</help>
    </field>
    <field>
        <id>rmmagent.general.AgentName</id>
        <label>Agent Name</label>
        <type>text</type>
        <help>Optionaler Name (Standard: Hostname der Firewall)</help>
    </field>
    <field>
        <id>rmmagent.general.Interval</id>
        <label>Heartbeat Intervall (Sek.)</label>
        <type>text</type>
        <help>Intervall in Sekunden (Standard: 60)</help>
    </field>
    <field>
        <id>rmmagent.general.Insecure</id>
        <label>Self-Signed Zertifikate</label>
        <type>checkbox</type>
        <help>TLS-Pruefung deaktivieren (fuer self-signed Backend-Zertifikate)</help>
    </field>
</form>
FORMEOF

# View
mkdir -p /usr/local/opnsense/mvc/app/views/OPNsense/RMMAgent
cat > /usr/local/opnsense/mvc/app/views/OPNsense/RMMAgent/general.volt << 'VOLTEOF'
<script>
    $(document).ready(function () {
        var data_get_map = {'frm_GeneralSettings': '/api/rmmagent/settings/get'};
        mapDataToFormUI(data_get_map).done(function () {
            formatTokenizersUI();
            $('.selectpicker').selectpicker('refresh');
        });
        ajaxCall('/api/rmmagent/service/status', {}, function (data, status) {
            if (data && data['status']) {
                var badge = data['status'] === 'running'
                    ? '<span class="label label-success">Running</span>'
                    : '<span class="label label-danger">Stopped</span>';
                $('#service_status_container').html(badge);
            }
        });
        function refreshStatus() {
            ajaxCall('/api/rmmagent/service/status', {}, function (data, status) {
                if (data && data['status']) {
                    var badge = data['status'] === 'running'
                        ? '<span class="label label-success">Running</span>'
                        : '<span class="label label-danger">Stopped</span>';
                    $('#service_status_container').html(badge);
                }
            });
        }
        $('#saveAct').unbind('click').click(function () {
            saveFormToEndpoint('/api/rmmagent/settings/set', 'frm_GeneralSettings', function () {
                $('#saveAct_progress').addClass('fa fa-spinner fa-pulse');
                ajaxCall('/api/rmmagent/service/reconfigure', {}, function (data, status) {
                    $('#saveAct_progress').removeClass('fa fa-spinner fa-pulse');
                    setTimeout(refreshStatus, 2000);
                });
            });
        });
        $('#restartAct').unbind('click').click(function () {
            $('#restartAct_progress').addClass('fa fa-spinner fa-pulse');
            ajaxCall('/api/rmmagent/service/restart', {}, function (data, status) {
                $('#restartAct_progress').removeClass('fa fa-spinner fa-pulse');
                setTimeout(refreshStatus, 2000);
            });
        });
    });
</script>
<div class="content-box" style="padding: 10px;">
    <div class="content-box-header">
        <h3>{{ lang._('RMM Agent') }} <small id="service_status_container"><span class="label label-default">...</span></small></h3>
    </div>
    <div class="content-box-main">
        <div class="table-responsive">
            {{ partial("layout_partials/base_form", ['fields': generalForm, 'id': 'frm_GeneralSettings']) }}
        </div>
    </div>
    <div class="col-md-12">
        <hr/>
        <button class="btn btn-primary" id="saveAct" type="button">
            <b>{{ lang._('Speichern & Anwenden') }}</b> <i id="saveAct_progress"></i>
        </button>
        <button class="btn btn-default" id="restartAct" type="button">
            <b>{{ lang._('Neustart') }}</b> <i id="restartAct_progress"></i>
        </button>
    </div>
</div>
VOLTEOF

# configd actions
echo "[5/8] configd Actions installieren..."
cat > /usr/local/opnsense/service/conf/actions.d/actions_rmmagent.conf << 'ACTEOF'
[start]
command:/usr/local/etc/rc.d/rmm_agent start
parameters:
type:script
message:Starting RMM Agent

[stop]
command:/usr/local/etc/rc.d/rmm_agent stop
parameters:
type:script
message:Stopping RMM Agent

[restart]
command:/usr/local/etc/rc.d/rmm_agent restart
parameters:
type:script
message:Restarting RMM Agent

[status]
command:/usr/local/etc/rc.d/rmm_agent status; exit 0
parameters:
type:script_output
message:Request RMM Agent status

[reconfigure]
command:/usr/local/opnsense/scripts/rmmagent/setup.sh
parameters:
type:script
message:Reconfiguring RMM Agent
ACTEOF

# Setup-Script
echo "[6/8] Setup-Script installieren..."
mkdir -p /usr/local/opnsense/scripts/rmmagent
cat > /usr/local/opnsense/scripts/rmmagent/setup.sh << 'SETUPEOF'
#!/bin/sh
CONFIGDIR="/usr/local/rmm"
CONFIGFILE="${CONFIGDIR}/config.yaml"

mkdir -p "${CONFIGDIR}"

# Config aus OPNsense Model generieren
BACKEND_URL=$(pluginctl -g OPNsense.rmmagent.general.BackendUrl 2>/dev/null)
API_KEY=$(pluginctl -g OPNsense.rmmagent.general.ApiKey 2>/dev/null)
INTERVAL=$(pluginctl -g OPNsense.rmmagent.general.Interval 2>/dev/null)
AGENT_NAME=$(pluginctl -g OPNsense.rmmagent.general.AgentName 2>/dev/null)
INSECURE=$(pluginctl -g OPNsense.rmmagent.general.Insecure 2>/dev/null)
ENABLED=$(pluginctl -g OPNsense.rmmagent.general.Enabled 2>/dev/null)

if [ -z "${INTERVAL}" ]; then
    INTERVAL="60"
fi

cat > "${CONFIGFILE}" << EOF
backend_url: "${BACKEND_URL}"
api_key: "${API_KEY}"
interval_seconds: ${INTERVAL}
EOF

if [ -n "${AGENT_NAME}" ]; then
    echo "agent_name: \"${AGENT_NAME}\"" >> "${CONFIGFILE}"
fi

# Insecure Flag
if [ "${INSECURE}" = "1" ]; then
    sysrc rmm_agent_insecure="YES" > /dev/null 2>&1
else
    sysrc rmm_agent_insecure="NO" > /dev/null 2>&1
fi

# Service steuern
if [ "${ENABLED}" = "1" ]; then
    sysrc rmm_agent_enable="YES" > /dev/null 2>&1
    /usr/local/etc/rc.d/rmm_agent restart
    echo "RMM Agent gestartet"
else
    /usr/local/etc/rc.d/rmm_agent stop 2>/dev/null
    sysrc rmm_agent_enable="NO" > /dev/null 2>&1
    echo "RMM Agent gestoppt"
fi
SETUPEOF
chmod +x /usr/local/opnsense/scripts/rmmagent/setup.sh

# Plugin Hook
echo "[7/8] Plugin-Hook registrieren..."
cat > /usr/local/etc/inc/plugins.inc.d/rmmagent.inc << 'INCEOF'
<?php
use OPNsense\RMMAgent\RMMAgent;

function rmmagent_services()
{
    $services = array();
    $mdl = new RMMAgent();
    if ((string)$mdl->general->Enabled == '1') {
        $services[] = array(
            'description' => gettext('RMM Agent'),
            'configd' => array(
                'restart' => array('rmmagent restart'),
                'start' => array('rmmagent start'),
                'stop' => array('rmmagent stop'),
            ),
            'name' => 'rmm_agent',
            'pidfile' => '/var/run/rmm_agent.pid',
        );
    }
    return $services;
}
INCEOF

# Initialen config.xml Eintrag erstellen (falls noch nicht vorhanden)
echo "  Initialen config.xml Eintrag pruefen..."
cat > /tmp/rmm_init_config.php << 'INITEOF'
<?php
require_once("/usr/local/etc/inc/config.inc");
$config = OPNsense\Core\Config::getInstance();
$xml = $config->object();
if (!isset($xml->OPNsense->rmmagent)) {
    $rmm = $xml->OPNsense->addChild('rmmagent');
    $gen = $rmm->addChild('general');
    $gen->addChild('Enabled', '0');
    $gen->addChild('BackendUrl', '');
    $gen->addChild('ApiKey', '');
    $gen->addChild('AgentName', '');
    $gen->addChild('Interval', '60');
    $gen->addChild('Insecure', '1');
    $config->save();
    echo "  Initialer rmmagent Eintrag in config.xml erstellt\n";
} else {
    echo "  rmmagent Eintrag bereits vorhanden\n";
}
INITEOF
php /tmp/rmm_init_config.php
rm -f /tmp/rmm_init_config.php

# Cache loeschen und configd neustarten
rm -f /var/lib/php/cache/_usr_local_opnsense_mvc_app_views_opnsense_rmmagent_*.php
service configd restart

# Updater aktivieren
echo "[8/8] Updater aktivieren..."
if [ -f /usr/local/rmm/rmm-updater ]; then
    sysrc rmm_updater_enable=YES 2>/dev/null || true
    /usr/local/etc/rc.d/rmm_updater start 2>/dev/null || true
    echo "  Updater aktiviert und gestartet"
else
    echo "  Kein Updater-Binary — uebersprungen"
fi

echo ""
echo "========================================"
echo " RMM Agent Plugin installiert!"
echo ""
echo " WebGUI: Services > RMM Agent"
echo " - Backend URL + API Key eintragen"
echo " - Aktiviert ankreuzen"
echo " - Speichern & Anwenden"
echo "========================================"
