<?php // tests/Feature/Cancellation/DashboardCountdownTest.php

use App\Livewire\Dashboard;
use App\Models\Customer;
use App\Models\Instance;
use App\Models\Order;
use App\Models\User;
use Livewire\Livewire;

/**
 * Ein Kunde mit einer gekündigten Instanz — nach dem Muster von
 * kuendbareInstanz() (tests/Feature/Cancellation/CancelAsksAboutExportTest.php):
 * Nutzer, Kunde, Bestellung, Instanz. Anders als dort schon im Zustand NACH
 * der Kündigung, so wie ihn ConfirmCancelPackage::cancelPackage() hinterlässt
 * — status und service_ends_at sind bereits gesetzt.
 *
 * @return array{0: User, 1: Instance}
 */
function gekuendigteInstanz(\Illuminate\Support\Carbon $endetIn, string $status = 'cancellation_scheduled'): array
{
    $user = User::factory()->create(['email' => 'gekuendigt@example.test', 'email_verified_at' => now()]);

    $customer = Customer::factory()->create([
        'email' => 'gekuendigt@example.test',
        'user_id' => $user->id,
        'status' => 'active',
    ]);

    $order = Order::factory()->create(['customer_id' => $customer->id, 'plan' => 'team']);

    $instance = Instance::factory()->create([
        'customer_id' => $customer->id,
        'order_id' => $order->id,
        'plan' => 'team',
        'status' => $status,
        'subdomain' => 'gekuendigt',
        'cancel_requested_at' => now()->subDays(2),
        'service_ends_at' => $endetIn,
    ]);

    return [$user, $instance->refresh()];
}

/**
 * Ein Kunde mit einer laufenden Instanz, die niemand gekündigt hat —
 * dieselbe Form wie gekuendigteInstanz(), nur ohne Kündigung.
 *
 * @return array{0: User, 1: Instance}
 */
function laufendeInstanz(): array
{
    $user = User::factory()->create(['email' => 'laeuft@example.test', 'email_verified_at' => now()]);

    $customer = Customer::factory()->create([
        'email' => 'laeuft@example.test',
        'user_id' => $user->id,
        'status' => 'active',
    ]);

    $order = Order::factory()->create(['customer_id' => $customer->id, 'plan' => 'team']);

    $instance = Instance::factory()->create([
        'customer_id' => $customer->id,
        'order_id' => $order->id,
        'plan' => 'team',
        'status' => 'active',
        'subdomain' => 'laeuft',
    ]);

    return [$user, $instance->refresh()];
}

it('zeigt nach einer Kuendigung, wie lange der Zugang noch steht', function () {
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12));

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSee(__('dashboard.ending.title'))
        ->assertSee($instance->service_ends_at->local()->isoFormat('LL'));
});

it('zeigt den Streifen nicht, solange nicht gekuendigt wurde', function () {
    [$user] = laufendeInstanz();

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertDontSee(__('dashboard.ending.title'));
});

it('zeigt ihn auch nach dem Laufzeitende nicht mehr', function () {
    // Danach ist der Zugang zu, und ein Streifen, der eine Restzeit nennt,
    // wäre schlicht falsch. Was dann gilt, sagt der Kasten aus der Prüfung
    // darunter — bis zur Fix-Welle behauptete dieser Kommentar das, und die
    // Seite sagte das Gegenteil.
    [$user] = gekuendigteInstanz(endetIn: now()->subDay(), status: 'ended');

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertDontSee(__('dashboard.ending.title'));
});

it('sagt nach dem Laufzeitende, dass die Laufzeit beendet ist', function () {
    // K2, zweite Hälfte: Dashboard::render() holt eine `ended`-Instanz nicht
    // mehr, und der Kunde fiel damit in denselben Zweig wie jemand, der noch
    // nie etwas bestellt hat — „Ihre Cloud wird eingerichtet." samt „Paket
    // buchen", am Tag, an dem ihm EndInstanceService die Adresse eingezogen
    // hat. Das ist kein Kunde ohne Paket, sondern einer, dessen Paket vorbei
    // ist, und die Seite muss das sagen, samt dem Datum.
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->subDay(), status: 'ended');

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSee(__('dashboard.ended.label'))
        ->assertSee($instance->service_ends_at->local()->isoFormat('LL'))
        ->assertDontSee(__('dashboard.title_pending'))
        ->assertDontSee(__('dashboard.no_instance_label'));
});

it('zeigt einem Kunden ohne jede Instanz weiterhin den leeren Zustand', function () {
    // Die Gegenprobe zur Prüfung darüber: der beendete Fall darf den ehrlichen
    // Leerzustand nicht verschlucken. Wer nie etwas bestellt hat, bekommt
    // weiterhin „Noch keine Instanz" — und keine Rede von einer Laufzeit, die
    // es nie gab.
    $user = User::factory()->create(['email' => 'ohne@example.test', 'email_verified_at' => now()]);
    Customer::factory()->create([
        'email' => 'ohne@example.test',
        'user_id' => $user->id,
        'status' => 'active',
    ]);

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSee(__('dashboard.no_instance_label'))
        ->assertDontSee(__('dashboard.ended.label'));
});

it('nennt keine Restzeit, die schon abgelaufen ist', function () {
    // Der Fall zwischen den Stühlen: das Laufzeitende ist vorbei, aber der
    // stündliche Lauf hat die Instanz noch nicht auf `ended` gestellt. Eine
    // Zeile "noch -3 Stunden" ist die Sorte Fehler, die niemand meldet und
    // jeder sieht.
    //
    // Korrektur gegenüber dem Zettel: `not->toContain('-')` über das ganze
    // Dokument trägt nicht — ein Datum und eine CSS-Klasse enthalten ebenso
    // Bindestriche, und die Prüfung wäre aus dem falschen Grund rot oder
    // grün. Sie zielt stattdessen auf die Restzeit selbst: der Streifen darf
    // gar nicht erst erscheinen, wenn das Laufzeitende vorbei ist — unabhängig
    // davon, ob der Status das schon nachvollzogen hat. Genau das ist die
    // Prüfung, die eine Implementierung ertappt, die sich allein auf den
    // Status verlässt und das Datum ignoriert.
    [$user] = gekuendigteInstanz(endetIn: now()->subHours(3));

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertDontSee(__('dashboard.ending.title'));
});

it('nennt die Restzeit in ganzen Tagen, solange mehr als ein Tag bleibt', function () {
    // Nicht im Zettel geprüft, aber ausdrücklich gefordert: "noch 12 Tage"
    // liest sich, "noch 11 Tage 7 Stunden" nicht — abgerundet auf ganze Tage.
    [$user] = gekuendigteInstanz(endetIn: now()->addDays(3)->addHours(5));

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSee(trans_choice('dashboard.ending.remaining_days', 3));
});

it('zaehlt am letzten Tag in Stunden, nicht mehr in Tagen', function () {
    [$user] = gekuendigteInstanz(endetIn: now()->addHours(5));

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSee(trans_choice('dashboard.ending.remaining_hours', 5));
});

/*
 * Die drei Zustände am Streifen, je einer pro Prüfung.
 *
 * Vorher stand hier EINE Prüfung, und sie konnte nicht fehlschlagen:
 * `x-ui.switch` rendert beide Wörter und überlässt dem CSS, welches sichtbar
 * ist — `assertSee('Kein Export gewünscht')` war deshalb bei `true`, bei
 * `false` UND bei `null` grün. Genau deshalb kam K1 durch: `(bool) null` ist
 * `false`, der Schalter behauptete einem nie gefragten Menschen seine eigene
 * Antwort, und nichts hielt die drei Zustände auseinander.
 *
 * Die Auswahlfelder tragen ihren Zustand dagegen im Markup (`checked`), also
 * prüft das hier wirklich das, was der Kunde sieht.
 */
it('zeigt am Streifen ein Ja als Ja', function () {
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12));
    $instance->update(['export_wish' => true]);

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSeeHtml('id="export-wish-yes" checked')
        ->assertDontSeeHtml('id="export-wish-no" checked');
});

it('zeigt am Streifen ein Nein als Nein', function () {
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12));
    $instance->update(['export_wish' => false]);

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertSeeHtml('id="export-wish-no" checked')
        ->assertDontSeeHtml('id="export-wish-yes" checked');
});

it('behauptet keine Antwort, wenn niemand gefragt wurde', function () {
    // Der Schaden, gegen den der Kopfkommentar der Wanderung geschrieben
    // wurde: eine vor dieser Ausrollung gekündigte Instanz trägt `null`, und
    // der Streifen sagte darunter „Kein Export gewünscht" — eine Aussage über
    // einen Wunsch, den dieser Mensch nie geäußert hat, dargestellt als seine
    // eigene Antwort. Keine Auswahl ist markiert, und der Satz daneben fragt.
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12));

    expect($instance->export_wish)->toBeNull();

    Livewire::actingAs($user)->test(Dashboard::class)
        ->assertDontSeeHtml('id="export-wish-yes" checked')
        ->assertDontSeeHtml('id="export-wish-no" checked')
        ->assertSee(__('dashboard.ending.export_open'));
});

it('laesst die Antwort am Streifen aendern', function () {
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12));
    $instance->update(['export_wish' => false]);

    Livewire::actingAs($user)->test(Dashboard::class)
        ->call('setExportWish', true);

    expect($instance->fresh()->export_wish)->toBeTrue();
});

it('laesst die Antwort nach dem Laufzeitende nicht mehr aendern', function () {
    // Aufgabe 4: danach ist die Instanz `ended`, die Adresse eingezogen, und
    // ob ein Export vorbereitet wurde, ist entschieden. Ein Aufruf, der dort
    // noch etwas verspricht, wäre die nächste Attrappe — geprüft direkt gegen
    // die Methode, nicht nur gegen den (dann ohnehin nicht mehr sichtbaren)
    // Schalter im Streifen: setExportWish() ist über /livewire/update auch am
    // Formular vorbei erreichbar.
    [$user, $instance] = gekuendigteInstanz(endetIn: now()->subDay(), status: 'ended');
    $instance->update(['export_wish' => false]);

    Livewire::actingAs($user)->test(Dashboard::class)
        ->call('setExportWish', true);

    expect($instance->fresh()->export_wish)->toBeFalse();
});
