<?php

namespace App\Mail;

use App\Mail\Concerns\RidesALane;
use App\Mail\Concerns\SendsFromMailbox;
use App\Models\Invoice;
use App\Services\Billing\InvoiceMath;
use App\Services\Billing\InvoiceRenderer;
use App\Services\Mail\MailCatalogue;
use App\Services\Mail\MailPurpose;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

/**
 * The invoice, with the invoice attached.
 *
 * The PDF is rendered when the mail is sent rather than fetched from anywhere,
 * because nothing stores it — and rendering from the frozen snapshot means the
 * attachment is the document as issued no matter when the queue gets round to
 * it, or how often it is retried.
 *
 * From the BILLING mailbox: a reply to this is a question about money.
 */
class InvoiceMail extends Mailable implements ShouldQueue
{
    use Queueable, RidesALane, SendsFromMailbox, SerializesModels;

    public function __construct(public Invoice $invoice, public string $name)
    {
        // The mailer, alongside the From. A purpose mailbox authenticates with
        // its OWN account, and a mail server lets an account send only from the
        // address it owns — so a From of billing@ over the default mailer's
        // no-reply@ login is answered "553 Sender address rejected: not owned by
        // user". Setting the From from a mailbox and leaving the mailer at the
        // default is a mail that renders perfectly and never arrives.
        $this->mailer(MailCatalogue::mailer('invoice'));
    }

    public function envelope(): Envelope
    {
        return $this->mailboxEnvelope(
            MailPurpose::BILLING,
            __('invoice_mail.subject', ['number' => $this->invoice->number]),
            'invoice',
        );
    }

    public function content(): Content
    {
        $meta = (array) ($this->invoice->snapshot['meta'] ?? []);

        return new Content(view: 'mail.invoice', with: [
            'name' => $this->name,
            'number' => $this->invoice->number,
            'issuedOn' => $this->invoice->issued_on?->local()->format('d.m.Y'),
            'dueOn' => $this->invoice->due_on?->local()->format('d.m.Y'),
            'gross' => InvoiceMath::money((int) $this->invoice->gross_cents),
            'currency' => $this->invoice->currency,
            'paymentTerms' => (string) ($meta['payment_terms'] ?? ''),
            'invoicesUrl' => route('invoices'),
        ]);
    }

    /** @return array<int, Attachment> */
    public function attachments(): array
    {
        return [
            // fromData, not fromPath: there is no path. The document exists as
            // a row and becomes a file only for as long as this mail needs one.
            Attachment::fromData(
                fn () => app(InvoiceRenderer::class)->forInvoice($this->invoice),
                $this->invoice->number.'.pdf',
            )->withMime('application/pdf'),
        ];
    }
}
