<?php

use App\Livewire\Admin\Incidents as IncidentConsole;
use App\Models\Incident;
use App\Support\LocalTime;
use Livewire\Livewire;

/**
 * Writing the record.
 *
 * The public page can only be as honest as what is typed here, so these tests
 * are about the two ways the console could make it dishonest: letting somebody
 * revise what was already said, and letting the incident and its timeline drift
 * apart.
 */
it('publishes an incident together with its first update, or not at all', function () {
    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(IncidentConsole::class)
        ->set('title', 'Instanzen nicht erreichbar')
        ->set('components', ['instances'])
        ->set('impact', 'down')
        ->set('startedAt', LocalTime::toField(now()->subMinutes(20)))
        ->set('firstUpdate', 'Mehrere Instanzen antworten nicht.')
        ->call('create')
        ->assertHasNoErrors();

    $incident = Incident::query()->firstOrFail();

    // A headline with nothing under it is what a reader would see if the
    // second insert were allowed to fail on its own.
    expect($incident->updates)->toHaveCount(1)
        ->and($incident->updates->first()->status)->toBe('investigating')
        ->and($incident->isPublished())->toBeTrue()
        ->and($incident->components)->toBe(['instances']);
});

it('refuses an incident that names no service', function () {
    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(IncidentConsole::class)
        ->set('title', 'Irgendwas')
        ->set('components', [])
        ->set('firstUpdate', 'Text')
        ->call('create')
        ->assertHasErrors('components');

    expect(Incident::query()->count())->toBe(0);
});

it('closes the incident with the same action that says it is resolved', function () {
    // Two separate actions is how a status page ends up with a "behoben" note
    // under an incident that still reports as ongoing.
    $incident = Incident::query()->create([
        'title' => 'x', 'impact' => 'down', 'components' => ['portal'],
        'started_at' => now()->subHour(), 'published_at' => now()->subHour(),
    ]);

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(IncidentConsole::class)
        ->call('startUpdate', $incident->uuid)
        ->set('updateStage', 'resolved')
        ->set('updateBody', 'Seit 20:51 stabil.')
        ->call('postUpdate')
        ->assertHasNoErrors();

    expect($incident->fresh()->isResolved())->toBeTrue()
        ->and($incident->fresh()->stage())->toBe('resolved');
});

it('takes a correction as a further entry, never as an edit', function () {
    // An update is a statement made at a time. There is deliberately no way
    // here to change one after the fact.
    $incident = Incident::query()->create([
        'title' => 'x', 'impact' => 'degraded', 'components' => ['backups'],
        'started_at' => now()->subHour(), 'published_at' => now()->subHour(),
    ]);
    $incident->updates()->create(['status' => 'investigating', 'body' => 'Erste Einschätzung.']);

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(IncidentConsole::class)
        ->call('startUpdate', $incident->uuid)
        ->set('updateStage', 'identified')
        ->set('updateBody', 'Korrektur: es betrifft nur einen Host.')
        ->call('postUpdate');

    $bodies = $incident->fresh()->updates->pluck('body');

    expect($bodies)->toHaveCount(2)
        ->and($bodies)->toContain('Erste Einschätzung.');

    expect(class_exists(App\Livewire\Admin\Incidents::class))->toBeTrue();
    expect(method_exists(App\Livewire\Admin\Incidents::class, 'editUpdate'))->toBeFalse();
});

it('needs the capability, not merely an operator account', function () {
    // Asserted on the PAGE rather than on the action: render() authorizes too,
    // so an operator without the capability never gets far enough to press a
    // button — and a Livewire::test() against a component that refuses to
    // render fails with a snapshot error that says nothing about permissions.
    $this->actingAs(operator('Support'), 'operator')
        ->get(route('admin.incidents'))
        ->assertForbidden();
});

it('records the operator who wrote each line', function () {
    // "Nachvollziehbar" includes who said it. Not shown publicly — this is for
    // the operator looking back at their own record months later.
    $owner = operator('Owner');

    Livewire::actingAs($owner, 'operator')
        ->test(IncidentConsole::class)
        ->set('title', 'x')
        ->set('components', ['portal'])
        ->set('startedAt', LocalTime::toField(now()))
        ->set('firstUpdate', 'Text')
        ->call('create');

    $incident = Incident::query()->firstOrFail();

    expect($incident->created_by)->toBe($owner->email)
        ->and($incident->updates->first()->created_by)->toBe($owner->email);
});
