<?php

use App\Models\Host;
use App\Provisioning\Jobs\CollectHostLoad;
use App\Services\Proxmox\FakeProxmoxClient;
use App\Services\Proxmox\HostLoadSeries;
use App\Services\Proxmox\ProxmoxClient;
use Illuminate\Contracts\Queue\ShouldBeUnique;

/**
 * Der Sammler holt die Stundenkurve dort, wo der Tunnel ist.
 *
 * Er existiert genau deshalb: nur der `provisioning`-Container hängt im
 * WireGuard-Netz. Der `app`-Container, der die Konsole rendert, erreicht die
 * Management-Adresse eines Hosts gar nicht — auf echter Hardware blieben
 * deshalb alle Kacheln leer, während der Host sichtbar online war.
 */
it('runs on the queue that can actually reach a host', function () {
    // Kein Detail: dieselbe Warteschlange wie PingHosts, aus demselben Grund.
    expect((new CollectHostLoad)->queue)->toBe('provisioning');
});

it('leaves a series behind for every host worth asking', function () {
    $s = fakeServices();
    $s['pve']->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
    $host = Host::factory()->active()->create(['api_token_ref' => 'ref']);

    (new CollectHostLoad)->handle(app(HostLoadSeries::class));

    expect(app(HostLoadSeries::class)->forHost($host)['available'])->toBeTrue();
});

it('skips a host that has no token to ask with', function () {
    // Ein Host mitten in der Übernahme hat noch keinen — ihn zu fragen wäre ein
    // sicherer Fehlschlag je Minute, und eine Zeile im Protokoll dazu.
    $s = fakeServices();
    $s['pve']->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
    $host = Host::factory()->create(['status' => 'onboarding', 'api_token_ref' => null]);

    (new CollectHostLoad)->handle(app(HostLoadSeries::class));

    expect(app(HostLoadSeries::class)->forHost($host)['available'])->toBeFalse();
});

it('keeps collecting after one host refuses to answer', function () {
    // Eine Flotte darf nicht daran hängen, dass der erste Host schweigt.
    $s = fakeServices();
    $s['pve']->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
    $silent = Host::factory()->active()->create(['api_token_ref' => 'ref-1', 'name' => 'aaa-still']);
    $healthy = Host::factory()->active()->create(['api_token_ref' => 'ref-2', 'name' => 'zzz-antwortet']);

    // Der Fake wirft für den ersten Host und antwortet für den zweiten.
    app()->instance(ProxmoxClient::class, new class($silent->id) extends FakeProxmoxClient
    {
        public function __construct(private int $silentId)
        {
            $this->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
        }

        public function nodeRrdData(string $node, string $timeframe = 'hour'): array
        {
            if ($this->host?->id === $this->silentId) {
                throw new RuntimeException('proxmox unreachable');
            }

            return $this->rrd;
        }
    });

    (new CollectHostLoad)->handle(app(HostLoadSeries::class));

    expect(app(HostLoadSeries::class)->forHost($silent)['available'])->toBeFalse()
        ->and(app(HostLoadSeries::class)->forHost($healthy)['available'])->toBeTrue();
});

it('never lets a second collector queue up behind a slow one', function () {
    // Die provisioning-Warteschlange ist DIESELBE, auf der Kunden-Bereitstellung
    // läuft. Ein Host, der in die Zeitüberschreitung läuft, kostet 15 Sekunden;
    // bei genug Hosts ist der minütliche Lauf noch nicht fertig, wenn der
    // nächste kommt — und der Rückstau verzögert dann bezahlte Arbeit.
    // Dasselbe Mittel wie bei CollectInstanceTraffic nebenan.
    expect(new CollectHostLoad)->toBeInstanceOf(ShouldBeUnique::class)
        // Und nur ein Versuch: eine verpasste Minute wird von der nächsten
        // Minute geholt, nicht von einer Wiederholung derselben.
        ->and((new CollectHostLoad)->tries)->toBe(1);
});
