<?php

use App\Services\Mail\MailCatalogue;
use Illuminate\Support\Facades\File;

/**
 * A From address belongs to the account that signs in.
 *
 * Every purpose mailbox in this installation has its OWN SMTP account, and a
 * mail server lets an account send only from the address it owns. mailcow
 * answers anything else with "553 5.7.1 Sender address rejected: not owned by
 * user no-reply@…" — and it is right to.
 *
 * So a mail that takes its From from a mailbox has to go out over that mailbox's
 * mailer as well. Setting one and forgetting the other produces a mail that
 * renders perfectly, queues without complaint, and never arrives: InvoiceMail
 * and OrderConfirmationMail both addressed billing@ while going out over the
 * default mailer's no-reply@ login, which means no customer had ever received an
 * invoice mail or an order confirmation on a server that checks.
 *
 * The same gap opened a second time, one level up. `mail.route.<mailart>` moves
 * ONE mail type to another mailbox — and it moved only the envelope: the
 * mailable still named `cp_<purpose>`, so `security-block` on info@ went out
 * with `From: info@` over no-reply@'s session. The pairing this file guards is
 * therefore no longer "From purpose == mailer purpose" but "From key == mailer
 * key", with MailCatalogue as the one place that maps a key to its purpose.
 */
it('gives every mail that takes a mailbox From the matching mailer', function () {
    $offenders = [];

    foreach (File::allFiles(app_path('Mail')) as $file) {
        $code = $file->getContents();

        // Only the ones that take their sender from a mailbox. A mailable with
        // no mailbox From has nothing to mismatch.
        if (! str_contains($code, 'mailboxEnvelope(') && ! str_contains($code, 'mailboxAddresses(')) {
            continue;
        }

        preg_match('/mailbox(?:Envelope|Addresses)\(\s*MailPurpose::([A-Z_]+)/', $code, $from);
        preg_match("/->mailer\(MailCatalogue::mailer\(('[a-z0-9-]+')/", $code, $keyed);
        preg_match("/->mailer\('cp_'\.MailPurpose::([A-Z_]+)\)/", $code, $byPurpose);

        $fromPurpose = $from[1] ?? null;

        if ($fromPurpose === null) {
            continue;
        }

        // The mailer names a mail type: the catalogue must agree that this type
        // belongs to the purpose the envelope asks for. Otherwise the fallback
        // when no route is set would move the sender without moving the login.
        //
        // A literal that is not itself a key is a PREFIX — DunningNoticeMail
        // builds 'dunning-'.$level, one class for four catalogued types. Then
        // every type behind that prefix has to agree, and there has to be at
        // least one: a prefix nothing matches is a mailer that does not exist.
        if (isset($keyed[1])) {
            $literal = trim($keyed[1], "'");
            $expected = constant('App\Services\Mail\MailPurpose::'.$fromPurpose);

            $matching = array_filter(
                MailCatalogue::all(),
                fn (string $key) => $key === $literal || str_starts_with($key, $literal),
                ARRAY_FILTER_USE_KEY,
            );

            if ($matching === []) {
                $offenders[] = $file->getFilename().": mail key [{$literal}] matches nothing in MailCatalogue";

                continue;
            }

            foreach ($matching as $key => $entry) {
                if ($entry['purpose'] !== $expected) {
                    $offenders[] = $file->getFilename().": From {$fromPurpose}, mail key {$key} is catalogued as {$entry['purpose']}";
                }
            }

            continue;
        }

        // No mail key at all (ContactRequestMail): the purpose mailer is still
        // the right answer, and it still has to match the From.
        if (($byPurpose[1] ?? null) !== $fromPurpose) {
            $offenders[] = $file->getFilename().': From '.$fromPurpose.', mailer '.($byPurpose[1] ?? 'DEFAULT');
        }
    }

    expect($offenders)->toBe([]);
});

it('sends each purpose over its own mailer, not over whatever mail.default is', function () {
    // The purpose mailers exist so that each mailbox authenticates as itself.
    // A mailable naming no mailer silently uses mail.default, which on this
    // installation is the plain smtp mailer with ONE account behind it.
    foreach (App\Services\Mail\MailPurpose::ALL as $purpose) {
        expect(config('mail.mailers.cp_'.$purpose))
            ->toBe(['transport' => 'mailbox', 'purpose' => $purpose]);
    }
});

it('gives every catalogued mail type a mailer of its own', function () {
    // Without the mailer, MailRoute would move the envelope and leave the SMTP
    // session where it was — the exact failure the route was built to allow.
    foreach (MailCatalogue::all() as $key => $entry) {
        expect(config('mail.mailers.'.MailCatalogue::mailer($key)))
            ->toBe(['transport' => 'mailbox', 'purpose' => $entry['purpose'], 'mail_key' => $key]);
    }
});
