<?php

use App\Models\Eventlog;
use LibreNMS\Exceptions\JsonAppException;
use LibreNMS\Exceptions\JsonAppMissingKeysException;
use LibreNMS\RRD\RrdDefinition;

$name = 'wireguard';
$output = 'OK';
$polling_type = 'app';

try {
    $interface_client_map = json_app_get($device, $name, 1)['data'];
} catch (JsonAppMissingKeysException $e) {
    $interface_client_map = $e->getParsedJson();
} catch (JsonAppException $e) {
    echo PHP_EOL .
        $name .
        ':' .
        $e->getCode() .
        ':' .
        $e->getMessage() .
        PHP_EOL;
    update_application($app, $e->getCode() . ':' . $e->getMessage(), []);

    return;
}

// RRD definition for interface+client metrics.
$rrd_def_intfclient = RrdDefinition::make()
    ->addDataset('bytes_rcvd', 'DERIVE', 0)
    ->addDataset('bytes_sent', 'DERIVE', 0)
    ->addDataset('minutes_since_last_handshake', 'GAUGE', 0);

// RRD definition for interface metrics.
$rrd_def_intf = RrdDefinition::make()
    ->addDataset('bytes_rcvd_total_intf', 'DERIVE', 0)
    ->addDataset('bytes_sent_total_intf', 'DERIVE', 0);

// RRD definition for global wireguard metrics.
$rrd_def_total = RrdDefinition::make()
    ->addDataset('bytes_rcvd_total', 'DERIVE', 0)
    ->addDataset('bytes_sent_total', 'DERIVE', 0);

$metrics = [];
$mappings = [];

$bytes_rcvd_total = null;
$bytes_sent_total = null;

// Parse json data for interfaces and their respective clients' metrics.
// Add any relevant data to the interface and global metrics within.
foreach ($interface_client_map as $interface => $client_list) {
    $bytes_rcvd_total_intf = null;
    $bytes_sent_total_intf = null;

    if (! is_string($interface)) {
        echo PHP_EOL .
            $name .
            ':' .
            ' Invalid or no interface found.' .
            PHP_EOL;

        continue;
    }
    $interface = \LibreNMS\Util\Clean::fileName($interface);

    $mappings[$interface] = [];
    foreach ($client_list as $client => $client_data) {
        if (! is_string($client)) {
            echo PHP_EOL .
                $name .
                ':' .
                ' Invalid or no client found.' .
                PHP_EOL;

            continue;
        }
        $client = \LibreNMS\Util\Clean::fileName($client);

        array_push($mappings[$interface], $client);
        $bytes_rcvd = is_numeric($client_data['bytes_rcvd'])
            ? $client_data['bytes_rcvd']
            : null;
        $bytes_sent = is_numeric($client_data['bytes_sent'])
            ? $client_data['bytes_sent']
            : null;
        $minutes_since_last_handshake = is_numeric(
            $client_data['minutes_since_last_handshake']
        )
            ? $client_data['minutes_since_last_handshake']
            : null;

        if (is_numeric($bytes_rcvd)) {
            $bytes_rcvd_total_intf += $bytes_rcvd;
            $bytes_rcvd_total += $bytes_rcvd;
        }

        if (is_numeric($bytes_sent)) {
            $bytes_sent_total_intf += $bytes_sent;
            $bytes_sent_total += $bytes_sent;
        }

        $fields_intfclient = [
            'bytes_rcvd' => $bytes_rcvd,
            'bytes_sent' => $bytes_sent,
            'minutes_since_last_handshake' => $minutes_since_last_handshake,
        ];

        // create flattened metrics
        $metrics['intf_' . $interface . '_client_' . $client] = $fields_intfclient;
        $tags_intfclient = [
            'name' => $name,
            'app_id' => $app->app_id,
            'rrd_def' => $rrd_def_intfclient,
            'rrd_name' => [
                $polling_type,
                $name,
                $app->app_id,
                $interface,
                $client,
            ],
        ];
        app('Datastore')->put($device, $polling_type, $tags_intfclient, $fields_intfclient);
    }

    // create interface fields
    $fields_intf = [
        'bytes_rcvd_total_intf' => $bytes_rcvd_total_intf,
        'bytes_sent_total_intf' => $bytes_sent_total_intf,
    ];

    // create interface metrics
    $metrics['intf_' . $interface] = $fields_intf;

    $tags_intf = [
        'name' => $name,
        'app_id' => $app->app_id,
        'rrd_def' => $rrd_def_intf,
        'rrd_name' => [$polling_type, $name, $app->app_id, $interface],
    ];
    app('Datastore')->put($device, $polling_type, $tags_intf, $fields_intf);
}

// create total fields
$fields_all = [
    'bytes_rcvd_total' => $bytes_rcvd_total,
    'bytes_sent_total' => $bytes_sent_total,
];

// create total metrics
$metrics['global'] = $fields_all;

$tags_all = [
    'name' => $name,
    'app_id' => $app->app_id,
    'rrd_def' => $rrd_def_total,
    'rrd_name' => [$polling_type, $name, $app->app_id],
];
app('Datastore')->put($device, $polling_type, $tags_all, $fields_all);

// variable tracks whether we updated mappings so it only happens once
$mappings_updated = false;

// get old mappings
$old_mappings = $app->data['mappings'] ?? [];

// update here even if there are no added or reel in any changes for table info display
$app->data = ['mappings' => $mappings, 'data' => $interface_client_map];

// check for interface changes
$added_interfaces = array_diff_key($mappings, $old_mappings);
$removed_interfaces = array_diff_key($old_mappings, $mappings);
if (count($added_interfaces) > 0 || count($removed_interfaces) > 0) {
    $mappings_updated = true;
    $log_message = 'Wireguard Interfaces Change:';
    $log_message .=
        count($added_interfaces) > 0
            ? ' Added ' . implode(',', array_keys($added_interfaces))
            : '';
    $log_message .=
        count($removed_interfaces) > 0
            ? ' Removed ' . implode(',', array_keys($removed_interfaces))
            : '';
    Eventlog::log($log_message, $device['device_id'], 'application');
}

// check for client changes
foreach ($mappings as $interface => $client_list) {
    $old_client_list = $old_mappings[$interface] ?? [];

    $added_clients = array_diff($client_list, $old_client_list);
    $removed_clients = array_diff($old_client_list, $client_list);
    if (count($added_clients) > 0 || count($removed_clients) > 0) {
        if (! $mappings_updated) {
            $app->data = ['mappings' => $mappings];
            $mappings_updated = true;
        }
        $log_message = 'Wireguard Interface ' . $interface . ' Clients Change:';
        $log_message .=
            count($added_clients) > 0
                ? ' Added ' . implode(',', $added_clients)
                : '';
        $log_message .=
            count($removed_clients) > 0
                ? ' Removed ' . implode(',', $removed_clients)
                : '';
        Eventlog::log($log_message, $device['device_id'], 'application');
    }
}

update_application($app, $output, $metrics);
