<?php

namespace App\Provisioning\Jobs;

use App\Models\Host;
use App\Services\Proxmox\HostLoadSeries;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

/**
 * Fetches the last hour of load from every host that is worth asking, and puts
 * it where the console can read it.
 *
 * On the `provisioning` queue, and that is the whole reason this job exists:
 * only that container is inside the WireGuard tunnel. The `app` container has
 * no route to a host's management address, so the console cannot fetch this
 * itself — it reads what this job left behind. Same rule, same queue and the
 * same reason as PingHosts next door.
 *
 * Every minute, because that is how often Proxmox writes a fresh sample. The
 * cache entry lives five minutes, so one missed run does not blank a page,
 * and a collector that has stopped takes the figures with it instead of
 * leaving an old hour on screen looking current.
 */
class CollectHostLoad implements ShouldBeUnique, ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * One collector at a time, fleet-wide.
     *
     * The `provisioning` queue is the SAME queue that carries out customer
     * provisioning. A host that has gone quiet costs this job the full HTTP
     * timeout, so with enough of them a minutely run is not finished when the
     * next one is dispatched — and the backlog then delays work somebody has
     * paid for. Same guard, same reason as CollectInstanceTraffic next door.
     */
    public int $uniqueFor = 600;

    /**
     * A missed minute is fetched by the next minute, not by a retry of this
     * one: the RRD is a rolling hour, so a repeat would ask for almost exactly
     * what the following run asks for anyway.
     */
    public int $tries = 1;

    public function __construct()
    {
        $this->onQueue('provisioning');
    }

    public function handle(HostLoadSeries $load): void
    {
        // Who counts as collectable lives on the model, in one place: this job
        // and the host page both read it, and when they disagreed, opening a
        // host mid-onboarding scanned the whole fleet and still left its tiles
        // empty.
        $hosts = Host::query()->collectable()->get();

        foreach ($hosts as $host) {
            // collect() logs its own failures and never throws: one unreachable
            // host must not stop the collection for the rest of the fleet.
            $load->collect($host);
        }
    }
}
