<?php

use App\Mail\ContactRequestMail;
use App\Models\Customer;
use App\Models\InboundMail;
use Illuminate\Support\Facades\Mail;

/**
 * Das Anfrageformular der öffentlichen Website.
 *
 * Es steht offen im Internet und verschickt eine Mail — die Grenzen sind hier
 * das Wesentliche, nicht die Felder.
 */
beforeEach(function () {
    Mail::fake();
});

it('zeigt das Formular', function () {
    $this->get(route('contact'))
        ->assertOk()
        ->assertSee('Sprechen Sie uns an.')
        ->assertSee('Anfrage senden');
});

it('stellt beim Thema Enterprise die Frage, um die es geht', function () {
    $this->get(route('contact', ['thema' => 'enterprise']))
        ->assertOk()
        ->assertSee('Eine eigene Maschine.')
        // Nicht ein leeres Feld hinstellen: wer hier landet, kam über den Knopf
        // unter den Paketen, und die zwei Zahlen entscheiden das Angebot.
        ->assertSee('Wie viel Speicher brauchen Sie', false);
});

it('fällt bei einem erfundenen Thema auf die allgemeine Fassung zurück', function () {
    // ?thema= steht in der Adresse und ist damit von jedem frei wählbar.
    $this->get(route('contact', ['thema' => '<script>']))
        ->assertOk()
        ->assertSee('Sprechen Sie uns an.');
});

it('schickt die Anfrage an das Haus und lässt die Antwort an den Fragenden gehen', function () {
    $this->post(route('contact.send'), [
        'company' => 'Kanzlei Muster',
        'name' => 'Anna Muster',
        'email' => 'anna@kanzlei-muster.at',
        'phone' => '+43 1 2345678',
        'message' => 'Wir haben 40 Mitarbeiter und brauchen etwa 400 GB.',
        'topic' => 'enterprise',
    ])->assertRedirect(route('contact'));

    Mail::assertQueued(ContactRequestMail::class, function (ContactRequestMail $mail) {
        $envelope = $mail->envelope();

        // Der Punkt der ganzen Mail: der Betreiber drückt „Antworten" und
        // schreibt der Person, nicht sich selbst.
        expect($envelope->replyTo[0]->address)->toBe('anna@kanzlei-muster.at');

        // Gesendet wird AUS dem eigenen Haus, nicht aus der fremden Domain —
        // sonst scheitert die Mail an SPF und DKIM und landet im Spam.
        expect($envelope->from->address)->not->toBe('anna@kanzlei-muster.at');

        // Betreff trägt Thema und Absender, damit die Postfachliste allein
        // schon sagt, worum es geht.
        expect($envelope->subject)->toBe('Anfrage: eigene Maschine — Kanzlei Muster');

        return true;
    });
});

it('nimmt den Namen in den Betreff, wenn keine Firma angegeben ist', function () {
    $this->post(route('contact.send'), [
        'name' => 'Bernd Beispiel',
        'email' => 'bernd@example.at',
        'message' => 'Ich hätte da eine Frage zu Ihren Paketen.',
    ]);

    Mail::assertQueued(ContactRequestMail::class, fn (ContactRequestMail $mail) => $mail->envelope()->subject === 'Anfrage über die Website — Bernd Beispiel');
});

it('bestätigt sichtbar und zeigt das Formular danach nicht mehr', function () {
    $this->post(route('contact.send'), [
        'name' => 'Anna Muster',
        'email' => 'anna@example.at',
        'message' => 'Bitte melden Sie sich bei mir.',
    ]);

    $this->followingRedirects()
        ->post(route('contact.send'), [
            'name' => 'Anna Muster',
            'email' => 'anna@example.at',
            'message' => 'Bitte melden Sie sich bei mir.',
        ])
        ->assertOk()
        ->assertSee('Ihre Anfrage ist da.')
        // Ein Formular, das nach dem Absenden weiter dasteht, lädt zum zweiten
        // Absenden ein.
        ->assertDontSee('Anfrage senden');
});

it('verlangt Name, Adresse und Nachricht', function () {
    $this->post(route('contact.send'), [])
        ->assertSessionHasErrors(['name', 'email', 'message']);

    Mail::assertNothingQueued();
});

it('lässt eine Nachricht aus zwei Wörtern nicht durch', function () {
    $this->post(route('contact.send'), [
        'name' => 'Anna', 'email' => 'anna@example.at', 'message' => 'Hallo',
    ])->assertSessionHasErrors(['message']);

    Mail::assertNothingQueued();
});

it('verwirft, was in den Honigtopf getappt ist', function () {
    // Das Feld ist im Browser nicht zu sehen. Wer es ausfüllt, ist kein Mensch.
    $this->post(route('contact.send'), [
        'name' => 'Robot', 'email' => 'robot@example.com',
        'message' => 'Kaufen Sie unsere Backlinks, sehr günstig.',
        'website' => 'http://spam.example.com',
    ])->assertSessionHasErrors(['website']);

    Mail::assertNothingQueued();
});

it('bremst den Versand, statt ihn offen stehen zu lassen', function () {
    // Ein Formular, das ohne Bremse Mail verschickt, ist ein Versandweg für
    // jeden, der es findet. Fünf in zehn Minuten, danach 429.
    $payload = ['name' => 'Anna', 'email' => 'anna@example.at', 'message' => 'Eine ganz normale Anfrage.'];

    for ($i = 0; $i < 5; $i++) {
        $this->post(route('contact.send'), $payload)->assertRedirect();
    }

    $this->post(route('contact.send'), $payload)->assertStatus(429);

    Mail::assertQueuedCount(5);
});

it('legt die Anfrage in den Posteingang der Konsole, nicht nur in eine Mail', function () {
    $this->post(route('contact.send'), [
        'company' => 'Kanzlei Muster',
        'name' => 'Anna Muster',
        'email' => 'anna@kanzlei-muster.at',
        'phone' => '+43 1 2345678',
        'message' => 'Wir haben 40 Mitarbeiter und brauchen etwa 400 GB.',
        'topic' => 'enterprise',
    ]);

    $eintrag = InboundMail::query()->latest('id')->first();

    expect($eintrag)->not->toBeNull()
        ->and($eintrag->from_email)->toBe('anna@kanzlei-muster.at')
        ->and($eintrag->from_name)->toBe('Anna Muster · Kanzlei Muster')
        ->and($eintrag->subject)->toBe('Anfrage: eigene Maschine — Kanzlei Muster')
        ->and($eintrag->body)->toContain('40 Mitarbeiter')
        // `inbound_mails` hat kein Feld für die Nummer. Sie im Formular zu
        // verlieren hieße, den Fragenden nicht anrufen zu können.
        ->and($eintrag->body)->toContain('+43 1 2345678')
        // Eine fremde Adresse bleibt ohne Zuordnung — und steht damit im
        // Posteingang ganz oben, wo eine neue Anfrage hingehört.
        ->and($eintrag->customer_id)->toBeNull();
});

it('hängt die Anfrage an den Kunden, wenn die Adresse schon bekannt ist', function () {
    $kunde = Customer::factory()->create(['email' => 'bestand@example.at']);

    $this->post(route('contact.send'), [
        'name' => 'Bestandskunde',
        'email' => 'bestand@example.at',
        'message' => 'Ich brauche mehr Speicher als mein Paket hergibt.',
    ]);

    expect(InboundMail::query()->latest('id')->value('customer_id'))->toBe($kunde->id);
});

it('behält die Anfrage, auch wenn der Versand scheitert', function () {
    // Der Fall, der das alles ausgelöst hat: support@ hatte kein eigenes
    // SMTP-Konto, der Server wies den Absender ab, die Anfrage lag in
    // `failed_jobs` — und der Besucher hatte „Ihre Anfrage ist da" gelesen.
    Mail::shouldReceive('send')->once()->andThrow(new RuntimeException('553 Sender address rejected'));

    // Der Versand fliegt dem Besucher um die Ohren, statt ihm eine Bestätigung
    // für etwas zu zeigen, das nicht angekommen ist.
    $this->post(route('contact.send'), [
        'name' => 'Anna Muster',
        'email' => 'anna@example.at',
        'message' => 'Bitte melden Sie sich bei mir.',
    ])->assertStatus(500);

    // Festgehalten, bevor überhaupt ein Versand versucht wurde.
    expect(InboundMail::query()->where('from_email', 'anna@example.at')->exists())->toBeTrue();
});

it('gibt zwei Anfragen zwei verschiedene Kennungen', function () {
    // message_id ist eindeutig. Eine zweite Anfrage darf nicht an der
    // Datenbank scheitern, weil beide dieselbe synthetische Kennung tragen.
    foreach (['eins@example.at', 'zwei@example.at'] as $adresse) {
        $this->post(route('contact.send'), [
            'name' => 'Anna', 'email' => $adresse, 'message' => 'Eine ganz normale Anfrage.',
        ])->assertRedirect();
    }

    expect(InboundMail::query()->count())->toBe(2);
});

it('trägt fremdes Markup nicht in die Mail, die unsere Adresse als Absender hat', function () {
    $this->post(route('contact.send'), [
        'name' => 'Anna Muster',
        'email' => 'anna@example.at',
        'message' => "Guten Tag\n<script>alert('x')</script>\nMit freundlichen Grüßen",
    ]);

    Mail::assertQueued(ContactRequestMail::class, function (ContactRequestMail $mail) {
        $html = $mail->render();

        return ! str_contains($html, "<script>alert('x')</script>")
            && str_contains($html, '&lt;script&gt;');
    });
});
