<?php

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

// admin() helper defined in HostManagementTest.

it('retries a failed run from the provisioning console', function () {
    Queue::fake();
    $host = Host::factory()->create(['status' => 'error']);
    $run = ProvisioningRun::factory()->create([
        'subject_type' => Host::class, 'subject_id' => $host->id, 'pipeline' => 'host',
        'status' => ProvisioningRun::STATUS_FAILED, 'attempt' => 3, 'current_step' => 4, 'error' => 'boom',
    ]);

    Livewire::actingAs(admin(), 'operator')->test(Provisioning::class)->call('retry', $run->uuid);

    $run->refresh();
    expect($run->status)->toBe(ProvisioningRun::STATUS_RUNNING)
        ->and($run->attempt)->toBe(0)
        ->and($run->error)->toBeNull()
        ->and($host->fresh()->status)->toBe('onboarding');
    Queue::assertPushed(AdvanceRunJob::class);
});

it('leaves a live customer alone when the run being retried is a maintenance one', function () {
    // The retry set the order to `provisioning` for ANY pipeline, and only the
    // `customer` pipeline ever puts it back to `active` (CompleteProvisioning).
    // So retrying a failed address, restart, storage, quota or plan-change run
    // left a paying customer's order reading `provisioning` for ever: nothing in
    // the product would move it again, the customer saw nothing wrong, and the
    // console's view of them was simply false. Same reasoning as RunRunner's
    // failRun() on the way down — a maintenance run must not condemn what it
    // maintains.
    Queue::fake();
    $order = Order::factory()->create(['status' => 'active']);
    $instance = Instance::factory()->create([
        'order_id' => $order->id, 'customer_id' => $order->customer_id, 'status' => 'active',
    ]);
    $run = ProvisioningRun::factory()->create([
        'subject_type' => Order::class, 'subject_id' => $order->id, 'pipeline' => 'address',
        'status' => ProvisioningRun::STATUS_FAILED, 'error' => 'boom',
    ]);

    Livewire::actingAs(admin(), 'operator')->test(Provisioning::class)->call('retry', $run->uuid);

    expect($run->fresh()->status)->toBe(ProvisioningRun::STATUS_RUNNING)
        ->and($order->fresh()->status)->toBe('active')
        ->and($instance->fresh()->status)->toBe('active');
    Queue::assertPushed(AdvanceRunJob::class);
});

it('does put a customer build back into provisioning when that is the run being retried', function () {
    Queue::fake();
    $order = Order::factory()->create(['status' => 'failed']);
    $instance = Instance::factory()->create([
        'order_id' => $order->id, 'customer_id' => $order->customer_id, 'status' => 'failed',
    ]);
    $run = ProvisioningRun::factory()->create([
        'subject_type' => Order::class, 'subject_id' => $order->id, 'pipeline' => 'customer',
        'status' => ProvisioningRun::STATUS_FAILED, 'error' => 'boom',
    ]);

    Livewire::actingAs(admin(), 'operator')->test(Provisioning::class)->call('retry', $run->uuid);

    expect($order->fresh()->status)->toBe('provisioning')
        ->and($instance->fresh()->status)->toBe('provisioning');
});

it('ignores retry on a run that is not failed', function () {
    Queue::fake();
    $host = Host::factory()->active()->create();
    $run = ProvisioningRun::factory()->create([
        'subject_type' => Host::class, 'subject_id' => $host->id, 'pipeline' => 'host',
        'status' => ProvisioningRun::STATUS_RUNNING,
    ]);

    Livewire::actingAs(admin(), 'operator')->test(Provisioning::class)->call('retry', $run->uuid);

    expect($run->fresh()->status)->toBe(ProvisioningRun::STATUS_RUNNING);
    Queue::assertNotPushed(AdvanceRunJob::class);
});

it('zaehlt einen fertigen Lauf in Liste und Karte gleich', function () {
    // Zwei Zahlen fuer denselben Lauf: in der Liste stand 15/16, in der Karte
    // daneben „16 von 16 abgeschlossen" — bei Status „Fertig" und 100 %. Die
    // Liste rechnete `current_step + 1`, also „dieser Schritt laeuft gerade";
    // bei einem fertigen Lauf laeuft aber keiner mehr.
    $host = Host::factory()->create(['status' => 'active']);
    $total = count(config('provisioning.pipelines.host'));

    ProvisioningRun::factory()->create([
        'subject_type' => Host::class, 'subject_id' => $host->id, 'pipeline' => 'host',
        'status' => ProvisioningRun::STATUS_COMPLETED,
        // Der letzte ausgefuehrte Schritt, NICHT die Gesamtzahl — genau der
        // Stand, bei dem die beiden Zahlen auseinanderliefen.
        'current_step' => $total - 2,
    ]);

    $rows = Livewire::actingAs(admin(), 'operator')->test(Provisioning::class)->viewData('rows');

    expect($rows[0]['n'])->toBe($total.'/'.$total)
        ->and($rows[0]['percent'])->toBe(100);
});

it('zeigt bei einem laufenden Lauf weiter den Schritt, der gerade dran ist', function () {
    // Die Gegenprobe: solange etwas laeuft, ist „dieser Schritt laeuft gerade"
    // genau richtig, und die Korrektur oben darf das nicht kaputtmachen.
    $host = Host::factory()->create(['status' => 'onboarding']);
    $total = count(config('provisioning.pipelines.host'));

    ProvisioningRun::factory()->create([
        'subject_type' => Host::class, 'subject_id' => $host->id, 'pipeline' => 'host',
        'status' => ProvisioningRun::STATUS_RUNNING, 'current_step' => 3,
    ]);

    $rows = Livewire::actingAs(admin(), 'operator')->test(Provisioning::class)->viewData('rows');

    expect($rows[0]['n'])->toBe('4/'.$total);
});
