<?php

use App\Models\Host;
use App\Models\Instance;
use App\Models\ProvisioningRun;
use App\Provisioning\Jobs\AdvanceRunJob;
use Illuminate\Support\Facades\Queue;

// QUEUE_CONNECTION ist in dieser Suite `sync`: ohne das hier faehrt jedes
// `AdvanceRunJob::dispatch()` den Lauf sofort im Test, gegen eine echte,
// unerreichbare Adresse. Die Laeufe, die dieser Test wirklich fahren will,
// stoesst er unten selbst an.
beforeEach(fn () => Queue::fake());

/**
 * Eine aktive Instanz auf einem Host mit vmid.
 *
 * Der Befehl ueberspringt Instanzen ohne Host oder ohne vmid. Ein Test, der
 * die Mailversand-Nachruestung fahren will, braucht beide.
 */
function aktiveInstanzMitHost(array $attributes = []): Instance
{
    $host = Host::factory()->active()->create(['datacenter' => 'fsn', 'node' => 'pve']);

    return Instance::factory()->create(array_merge([
        'host_id' => $host->id,
        'vmid' => 101,
        'status' => 'active',
    ], $attributes));
}

it('startet je aktiver Instanz einen Lauf', function () {
    $i1 = aktiveInstanzMitHost();
    $i2 = aktiveInstanzMitHost(['vmid' => 102]);
    Instance::factory()->create(['status' => 'closed']);

    $this->artisan('clupilot:configure-instance-mail')->assertSuccessful();

    $runs = ProvisioningRun::where('pipeline', 'instance-mail')->get();
    expect($runs->count())->toBe(2)
        ->and($runs[0]->status)->toBe(ProvisioningRun::STATUS_PENDING)
        ->and($runs[1]->status)->toBe(ProvisioningRun::STATUS_PENDING);

    // Der Auftrag muss mit der uuid des Laufs dispatched werden, nicht mit id.
    // Ohne uuid findet AdvanceRunJob keine Zeile und der Lauf steht für immer auf running.
    Queue::assertPushed(AdvanceRunJob::class, fn ($job) => $job->runUuid === $runs[0]->uuid);
    Queue::assertPushed(AdvanceRunJob::class, fn ($job) => $job->runUuid === $runs[1]->uuid);
});

it('faengt unter --dry-run gar nichts an', function () {
    aktiveInstanzMitHost();
    aktiveInstanzMitHost(['vmid' => 102]);
    aktiveInstanzMitHost(['vmid' => 103]);

    $this->artisan('clupilot:configure-instance-mail', ['--dry-run' => true])->assertSuccessful();

    expect(ProvisioningRun::where('pipeline', 'instance-mail')->count())->toBe(0);
});

it('nimmt mit --instance genau eine', function () {
    $eine = aktiveInstanzMitHost();
    aktiveInstanzMitHost(['vmid' => 102]);

    $this->artisan('clupilot:configure-instance-mail', ['--instance' => $eine->uuid])->assertSuccessful();

    expect(ProvisioningRun::where('pipeline', 'instance-mail')->count())->toBe(1);
});

it('ueberspringt eine Instanz ohne Host und sagt es', function () {
    Instance::factory()->create(['status' => 'active', 'host_id' => null, 'vmid' => 101]);

    $this->artisan('clupilot:configure-instance-mail')
        ->expectsOutputToContain('kein Host')
        ->assertSuccessful();

    expect(ProvisioningRun::where('pipeline', 'instance-mail')->count())->toBe(0);
});

it('ueberspringt eine Instanz ohne vmid und sagt es', function () {
    $host = Host::factory()->active()->create(['datacenter' => 'fsn', 'node' => 'pve']);
    Instance::factory()->create(['status' => 'active', 'host_id' => $host->id, 'vmid' => null]);

    $this->artisan('clupilot:configure-instance-mail')
        ->expectsOutputToContain('keine VMID')
        ->assertSuccessful();

    expect(ProvisioningRun::where('pipeline', 'instance-mail')->count())->toBe(0);
});

it('ueberspringt eine Instanz mit laufendem Lauf und sagt es', function () {
    $instance = aktiveInstanzMitHost();

    // Ein bereits laufender Lauf gegen diese Instanz
    ProvisioningRun::factory()->create([
        'subject_type' => Instance::class,
        'subject_id' => $instance->id,
        'status' => ProvisioningRun::STATUS_RUNNING,
    ]);

    $this->artisan('clupilot:configure-instance-mail')
        ->expectsOutputToContain('anderer Lauf aktiv')
        ->assertSuccessful();

    // Kein neuer Lauf angelegt
    expect(ProvisioningRun::where('pipeline', 'instance-mail')->count())->toBe(0);
});

it('zaehlt die Laeufe korrekt in der Abschlusszusammenfassung', function () {
    aktiveInstanzMitHost();
    aktiveInstanzMitHost(['vmid' => 102]);
    aktiveInstanzMitHost(['vmid' => 103]);

    // Eine Instanz ohne Host (wird uebergangen)
    Instance::factory()->create(['status' => 'active', 'host_id' => null, 'vmid' => 104]);

    $this->artisan('clupilot:configure-instance-mail')
        // Genau 3 Laeufe, nicht 6 wegen Doppelzaehlung
        ->expectsOutputToContain('3 Lauf/Läufe gestartet')
        ->expectsOutputToContain('übersprungen (kein Host): 1')
        ->assertSuccessful();
});

it('zaehlt unter --dry-run richtig', function () {
    aktiveInstanzMitHost();
    aktiveInstanzMitHost(['vmid' => 102]);

    // Eine Instanz ohne VMID (wird uebergangen)
    $host = Host::factory()->active()->create(['datacenter' => 'fsn', 'node' => 'pve']);
    Instance::factory()->create(['status' => 'active', 'host_id' => $host->id, 'vmid' => null]);

    $this->artisan('clupilot:configure-instance-mail', ['--dry-run' => true])
        // Genau 2 Instanzen im Probelauf
        ->expectsOutputToContain('Probelauf: 2 Instanz(en) bekämen')
        ->expectsOutputToContain('übersprungen (keine VMID): 1')
        ->assertSuccessful();
});
