<?php

// Zabbix API connection settings
define('ZABBIX_URL', 'http://localhost/zabbix70/');
define('ZABBIX_USER', 'Admin');
define('ZABBIX_PASS', 'zabbix');

define('DRULES_COUNT', 100); // total discovery rules to create
define('BATCH_SIZE', 50); // rules per API call

function api_request(string $url, string $method, array $params, ?string $auth = null): mixed {
    $payload = [
        'jsonrpc' => '2.0',
        'method'  => $method,
        'params'  => $params,
        'id'      => 1,
    ];

    $headers = ['Content-Type: application/json'];
    if ($auth !== null) {
        $headers[] = 'Authorization: Bearer ' . $auth;
    }

    $ch = curl_init($url . '/api_jsonrpc.php');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => $headers,
    ]);

    $response = curl_exec($ch);
    $err = curl_error($ch);
    curl_close($ch);

    // echo "[DEBUG] Request:  " . json_encode($payload, JSON_PRETTY_PRINT) . "\n";
    echo "[DEBUG] Response: " . $response . "\n\n";

    if ($err) {
        throw new RuntimeException("cURL error: $err");
    }

    $decoded = json_decode($response, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new RuntimeException("Invalid JSON response: $response");
    }
    if (isset($decoded['error'])) {
        throw new RuntimeException("API error [{$decoded['error']['code']}]: {$decoded['error']['data']}");
    }

    return $decoded['result'];
}

function login(string $url, string $user, string $pass): string {
    return api_request($url, 'user.login', ['username' => $user, 'password' => $pass]);
}

function logout(string $url, string $auth): void {
    api_request($url, 'user.logout', [], $auth);
}

function create_drules_batch(string $url, string $auth, array $drules): array {
    return api_request($url, 'drule.create', $drules, $auth);
}

function get_drules(string $url, string $auth, string $name_pattern): array {
    return api_request($url, 'drule.get', [
        'output' => ['druleid', 'name'],
        'search' => ['name' => $name_pattern],
    ], $auth);
}

function delete_drules(string $url, string $auth, array $druleids): array {
    return api_request($url, 'drule.delete', $druleids, $auth);
}

function build_drule(int $index): array {
    // Vary the subnet across rules to keep IP ranges unique
    $subnet_b = intdiv($index, 254) + 168;
    $subnet_c = ($index % 254) + 1;

    $subnet_b = 168;
    $subnet_c = 132;

    return [
        'name'    => sprintf('_Discovery Rule %04d', $index),
        'iprange' => sprintf('192.%d.%d.1-50,127.0.0.1-4', $subnet_b, $subnet_c),
        // 'iprange' => sprintf('127.0.0.1-2', $subnet_b, $subnet_c),
        'delay'   => '30',
        'status'  => 0, // 0 = enabled, 1 = disabled
        'dchecks' => [
            [
                'type'               => 13, // SNMPv3
                'ports'              => '161',
                'key_'               => '1.3.6.1.2.1.1.1.0', // sysDescr OID
                'snmpv3_securityname' => 'zabbix1',
                'snmpv3_securitylevel' => 2,
                'snmpv3_authprotocol' => 0,
                'snmpv3_authpassphrase' => 'zabbixAuth',
                'snmpv3_privprotocol' => 0,
                'snmpv3_privpassphrase' => 'zabbixPriv',
                'snmpv3_contextname' => 'recorded/linksys-system',
                'name_source'        => 0,
                'uniq'               => 0,
            ],
            [
                'type'               => 13, // SNMPv3
                'ports'              => '161',
                'key_'               => '1.3.6.1.2.1.1.5.0', // sysName OID
                'snmpv3_securityname' => 'zabbix2',
                'snmpv3_securitylevel' => 2,
                'snmpv3_authprotocol' => 0,
                'snmpv3_authpassphrase' => 'zabbixAuth2',
                'snmpv3_privprotocol' => 0,
                'snmpv3_privpassphrase' => 'zabbixPriv2',
                'snmpv3_contextname' => 'recorded/linksys-system',
                'name_source'        => 3,
                'host_source'        => 3,
                'uniq'               => 1,
            ],
            [
                'type'  => 8, // TCP
                'ports' => '10050',
            ],
            [
                'type'  => 9, // Zabbix agent
                'ports' => '10050',
                'key_'  => 'system.uname',
            ],
        ],
    ];
}

function build_drule2(int $index): array {
    // Vary the subnet across rules to keep IP ranges unique
    $subnet_b = intdiv($index, 254) + 168;
    $subnet_c = ($index % 254) + 1;

    $subnet_b = 168;
    $subnet_c = 132;

    return [
        'name'    => sprintf('_Discovery Rule %04d', $index),
        // 'iprange' => sprintf('192.%d.%d.1-50,127.0.0.1-4', $subnet_b, $subnet_c),
        'iprange' => '127.0.0.1',
        'delay'   => '30',
        'status'  => 0, // 0 = enabled, 1 = disabled
        'dchecks' => [
            [
                'type'               => 13, // SNMPv3
                'ports'              => '10100-10200',
                'key_'               => '1.3.6.1.2.1.1.1.0', // sysDescr OID
                'snmpv3_securityname' => 'usersingle',
                'snmpv3_securitylevel' => 2,
                'snmpv3_authprotocol' => 0,
                'snmpv3_authpassphrase' => 'authpass1',
                'snmpv3_privprotocol' => 0,
                'snmpv3_privpassphrase' => 'privpass1',
                'snmpv3_contextname' => '',
                'name_source'        => 0,
                'uniq'               => 0,
            ],
            [
                'type'               => 13, // SNMPv3
                'ports'              => '10100-10200',
                'key_'               => '1.3.6.1.2.1.1.5.0', // sysName OID
                'snmpv3_securityname' => 'usersingle',
                'snmpv3_securitylevel' => 2,
                'snmpv3_authprotocol' => 0,
                'snmpv3_authpassphrase' => 'authpass2',
                'snmpv3_privprotocol' => 0,
                'snmpv3_privpassphrase' => 'privpass2',
                'snmpv3_contextname' => '',
                'name_source'        => 3,
                'host_source'        => 3,
                'uniq'               => 1,
            ],
            [
                'type'  => 8, // TCP
                'ports' => '10050',
            ],
            [
                'type'  => 9, // Zabbix agent
                'ports' => '10050',
                'key_'  => 'system.uname',
            ],
        ],
    ];
}

function build_drule3(int $index): array {
    // Vary the subnet across rules to keep IP ranges unique
    $subnet_b = intdiv($index, 254) + 168;
    $subnet_c = ($index % 254) + 1;

    $subnet_b = 168;
    $subnet_c = 132;

    return [
        'name'    => sprintf('_Discovery Rule %04d', $index),
        'iprange' => '127.0.0.1',
        'delay'   => '30',
        'status'  => 0, // 0 = enabled, 1 = disabled
        'dchecks' => [
            [
                'type'               => 11, // SNMPv2c
                'ports'              => '10100-12100',
                'key_'               => '1.3.6.1.2.1.1.1.0', // sysDescr OID
                'snmp_community'     => 'public',
                'name_source'        => 0,
                'uniq'               => 0,
            ],
            [
                'type'               => 11, // SNMPv2c
                'ports'              => '10100-12100',
                'key_'               => '1.3.6.1.2.1.1.5.0', // sysName OID
                'snmp_community'     => 'public',
                'name_source'        => 3,
                'host_source'        => 3,
                'uniq'               => 1,
            ],
            [
                'type'  => 8, // TCP
                'ports' => '10050',
                'uniq'  => 0,
            ],
            [
                'type'  => 9, // Zabbix agent
                'ports' => '10050',
                'key_'  => 'system.uname',
                'uniq'  => 0,
            ],
        ],
    ];
}

function build_drule4(int $index): array {
    // Vary the subnet across rules to keep IP ranges unique
    $subnet_b = intdiv($index, 254) + 168;
    $subnet_c = ($index % 254) + 1;

    $subnet_b = 168;
    $subnet_c = 132;

    $port = 10160 + $index; // Unique port per rule to ensure unique SNMP checks

    return [
        'name'    => sprintf('_Discovery Rule %04d', $index),
        'iprange' => sprintf('192.%d.%d.1-50,127.0.0.1-4', $subnet_b, $subnet_c),
    
        'delay'   => '30',
        'status'  => 0, // 0 = enabled, 1 = disabled
        'dchecks' => [
            [
                'type'               => 11, // SNMPv2c
                'ports'              => $port,
                'key_'               => '1.3.6.1.2.1.1.1.0', // sysDescr OID
                'snmp_community'     => 'public',
                'name_source'        => 0,
                'uniq'               => 0,
            ],
            [
                'type'               => 11, // SNMPv2c
                'ports'              => $port,
                'key_'               => '1.3.6.1.2.1.1.5.0', // sysName OID
                'snmp_community'     => 'public',
                'name_source'        => 3,
                'host_source'        => 3,
                'uniq'               => 1,
            ],
            [
                'type'  => 8, // TCP
                'ports' => '10050',
            ],
            [
                'type'  => 9, // Zabbix agent
                'ports' => '10050',
                'key_'  => 'system.uname',
            ],
        ],
    ];
}

function build_drule5(int $index): array {
    // Vary the subnet across rules to keep IP ranges unique
    $subnet_b = intdiv($index, 254) + 168;
    $subnet_c = ($index % 254) + 1;

    $subnet_b = 168;
    $subnet_c = 132;

    return [
        'name'    => sprintf('_Discovery Rule %04d', $index),
        // 'iprange' => sprintf('127.%d.%d.1-50,127.0.0-3.1-100', $subnet_b, $subnet_c),
        'iprange' => sprintf('127.0.3-3.1-100', $subnet_b, $subnet_c),
        'delay'   => '30',
        'status'  => 0, // 0 = enabled, 1 = disabled
        'dchecks' => [
            [
                'type'               => 11, // SNMPv2c
                'ports'              => '10161',
                'key_'               => '1.3.6.1.2.1.1.1.0', // sysDescr OID
                'snmp_community'     => 'public',
                'name_source'        => 0,
                'uniq'               => 0,
            ],
            [
                'type'               => 11, // SNMPv2c
                'ports'              => '10161',
                'key_'               => '1.3.6.1.2.1.1.5.0', // sysName OID
                'snmp_community'     => 'public',
                'name_source'        => 3,
                'host_source'        => 3,
                'uniq'               => 1,
            ],
            // [
            //     'type'  => 8, // TCP
            //     'ports' => '10050',
            //     'uniq'  => 0,
            // ],
            // [
            //     'type'  => 9, // Zabbix agent
            //     'ports' => '10050',
            //     'key_'  => 'system.uname',
            //     'uniq'  => 0,
            // ],
        ],
    ];
}


// ── Main ─────────────────────────────────────────────────────────────────────

echo "Connecting to Zabbix at " . ZABBIX_URL . " ...\n";

try {
    $auth = login(ZABBIX_URL, ZABBIX_USER, ZABBIX_PASS);
    echo "Authenticated.\n\n";

    // Remove existing discovery rules if they exist
    $existing_drules = get_drules(ZABBIX_URL, $auth, 'Discovery Rule');
    if (!empty($existing_drules)) {
        $druleids = array_column($existing_drules, 'druleid');
        echo "Found " . count($druleids) . " existing discovery rules. Deleting...\n";
        delete_drules(ZABBIX_URL, $auth, $druleids);
        echo "Deleted existing discovery rules.\n\n";
    }

    $created = 0;
    $batch   = [];

    for ($i = 1; $i <= DRULES_COUNT; $i++) {
        // $batch[] = build_drule($i);
        // $batch[] = build_drule2($i);
        $batch[] = build_drule5($i);

        if (count($batch) === BATCH_SIZE || $i === DRULES_COUNT) {
            $result   = create_drules_batch(ZABBIX_URL, $auth, $batch);
            $count    = count($result['druleids'] ?? []);
            $created += $count;
            printf("  Created %4d / %d  (this batch: %d)\n", $created, DRULES_COUNT, $count);
            $batch = [];
        }
    }

    echo "\nDone. Total discovery rules created: $created\n";

    logout(ZABBIX_URL, $auth);
    echo "Logged out.\n";

} catch (RuntimeException $e) {
    echo "ERROR: " . $e->getMessage() . "\n";
    exit(1);
}

