<?php

use App\Actions\StartCustomerProvisioning;
use App\Mail\OrderConfirmationMail;
use App\Models\Customer;
use App\Models\Order;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;

/**
 * The mails this product sends, and the one layout they all wear.
 */
it('sends an order confirmation when no invoice can be issued', function () {
    // The company details are not filled in here, so IssueInvoice refuses —
    // correctly, because an invoice without a registered name or VAT number is
    // not a valid invoice. The purchase still stands and the customer is still
    // told.
    Mail::fake();
    Queue::fake();

    app(StartCustomerProvisioning::class)->fromStripeEvent([
        'id' => 'evt_'.uniqid(),
        'email' => 'kunde@example.com',
        'name' => 'Beispiel GmbH',
        'stripe_customer_id' => null,
        'plan' => 'start',
        'datacenter' => 'fsn',
        'amount_cents' => 1900,
        'currency' => 'EUR',
    ]);

    Mail::assertQueued(OrderConfirmationMail::class, function (OrderConfirmationMail $mail) {
        return $mail->hasTo('kunde@example.com');
    });
});

it('issues an invoice and sends it, instead of a bare confirmation, once it can', function () {
    // ONE mail, not two. The invoice is issued the moment the money lands, so
    // an order confirmation and an invoice mail would otherwise go out in the
    // same second for the same event.
    Mail::fake();
    Queue::fake();

    App\Support\CompanyProfile::put([
        'name' => 'CluPilot Cloud e.U.', 'address' => 'Dreherstraße 66/1/8',
        'postcode' => '1110', 'city' => 'Wien', 'vat_id' => 'ATU00000000',
    ]);

    app(StartCustomerProvisioning::class)->fromStripeEvent([
        'id' => 'evt_'.uniqid(),
        'email' => 'kunde@example.com',
        'name' => 'Beispiel GmbH',
        'stripe_customer_id' => null,
        'plan' => 'start',
        'datacenter' => 'fsn',
        'amount_cents' => 1900,
        'currency' => 'EUR',
    ]);

    expect(App\Models\Invoice::query()->count())->toBe(1);

    Mail::assertQueued(App\Mail\InvoiceMail::class);
    Mail::assertNotQueued(OrderConfirmationMail::class);
});

it('does not confirm the same order twice when Stripe delivers twice', function () {
    // Stripe retries until it gets a 2xx, so duplicate deliveries are ordinary
    // rather than exceptional. A second confirmation for one payment reads as a
    // second charge.
    Mail::fake();
    Queue::fake();

    $event = [
        'id' => 'evt_duplicate',
        'email' => 'kunde@example.com',
        'name' => 'Beispiel GmbH',
        'stripe_customer_id' => null,
        'plan' => 'start',
        'datacenter' => 'fsn',
        'amount_cents' => 1900,
        'currency' => 'EUR',
    ];

    app(StartCustomerProvisioning::class)->fromStripeEvent($event);
    app(StartCustomerProvisioning::class)->fromStripeEvent($event);

    Mail::assertQueuedCount(1);
});

it('renders the order confirmation without a template error, and with the money in it', function () {
    // Rendering is the only way to find a Blade mistake in a mail: nothing else
    // ever opens these files, and the first person who would is a customer.
    $customer = Customer::factory()->create(['name' => 'Beispiel GmbH']);
    $order = Order::factory()->create([
        'customer_id' => $customer->id,
        'amount_cents' => 1900,
        'currency' => 'EUR',
        'datacenter' => 'fsn',
    ]);

    $rendered = (new OrderConfirmationMail($order, 'Beispiel GmbH'))->render();

    expect($rendered)->toContain('19,00 EUR')
        ->and($rendered)->toContain(__('orders.mail_heading'))
        // The footer every mail carries, from the shared layout.
        ->and($rendered)->toContain(__('mail.why_you_got_this'));
});

it('keeps every mail template on the shared layout', function () {
    // Laravel's markdown component brings its own logo, button and footer. Two
    // templates were still wearing it, and a new one written in a hurry is one
    // copy-paste away from doing the same — at which point the product sends
    // mail that looks like two different companies.
    $offenders = [];

    foreach (File::allFiles(resource_path('views/mail')) as $file) {
        $contents = File::get($file->getPathname());

        if (str_contains($contents, 'x-mail::message') || str_contains($contents, 'x-mail::')) {
            $offenders[] = $file->getFilename();
        }
    }

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

it('never puts a credential in the mail that announces a finished cloud', function () {
    // The initial admin password used to travel there in clear text, across
    // third-party mail servers and then into an inbox for ever. It waits behind
    // the sign-in now, and this is what stops it drifting back.
    //
    // Asserted against the data the mail is actually given, not against the
    // template's source. A source scan trips over the word in a comment and
    // passes a template that leaks the value through a variable — it tests the
    // prose, not the mail.
    $secret = 'Str3ng-Geheim-'.uniqid();

    $customer = Customer::factory()->create();
    $instance = App\Models\Instance::factory()->create([
        'customer_id' => $customer->id,
        'admin_password' => $secret,
        'subdomain' => 'beispiel',
    ]);

    $user = App\Models\User::factory()->create(['name' => 'Beispiel GmbH']);

    $message = (new App\Notifications\CloudReady($instance))->toMail($user);

    expect(json_encode($message->viewData))->not->toContain($secret);

    // And the same for what is rendered from it, so a template that reaches
    // past its data for the model cannot slip through either.
    expect(view($message->view, $message->viewData)->render())->not->toContain($secret);
});
