<?php

namespace App\Console\Commands;

use App\Events\MetricsTicked;
use App\Models\Server;
use App\Services\FleetService;
use App\Support\Ssh\CredentialVault;
use App\Support\Ssh\SshClient;
use Illuminate\Console\Command;
use Throwable;

/**
 * Replaces the dev mock emitter: polls REAL CPU/MEM/disk over SSH for every
 * server that has a stored credential, persists the values onto the Server row,
 * and broadcasts MetricsTicked so the dashboard chart moves with real data.
 *
 * One long-lived SSH connection is kept per server and reused across ticks (a
 * single login, not one per interval — avoids fail2ban/auth-log churn). A dead
 * connection is dropped and re-established on the next tick; one server failing
 * never kills the loop.
 */
class PollMetrics extends Command
{
    protected $signature = 'clusev:poll-metrics
        {--interval=15 : Seconds between polls}
        {--once : Poll a single round and exit}';

    protected $description = 'Poll real metrics over SSH for credentialed servers, persist + broadcast';

    public function handle(FleetService $fleet, CredentialVault $vault): int
    {
        $interval = max(2, (int) $this->option('interval'));

        /** @var array<int, SshClient> $clients */
        $clients = [];

        do {
            $servers = Server::has('credential')->get();

            if ($servers->isEmpty()) {
                $this->warn('Keine Server mit Credential — nichts zu pollen.');
            }

            foreach ($servers as $server) {
                try {
                    $clients[$server->id] ??= (new SshClient($vault, timeout: 12))->connect($server);

                    $m = $fleet->readMetrics($clients[$server->id]);
                    $fleet->applyMetrics($server, $m);
                    broadcast(new MetricsTicked($m['cpu'], $m['mem'], $server->name));

                    $this->line("ok {$server->name} cpu={$m['cpu']} mem={$m['mem']} disk={$m['disk']} load={$m['load']}");
                } catch (Throwable $e) {
                    // drop the (possibly dead) connection so the next tick reconnects
                    if (isset($clients[$server->id])) {
                        $clients[$server->id]->disconnect();
                        unset($clients[$server->id]);
                    }
                    $server->forceFill(['status' => 'offline'])->save();
                    $this->warn("offline {$server->name}: ".$e->getMessage());
                }
            }

            if ($this->option('once')) {
                break;
            }

            sleep($interval);
        } while (true);

        foreach ($clients as $client) {
            $client->disconnect();
        }

        return self::SUCCESS;
    }
}
