<?php

namespace App\Http\Controllers;

use App\Filament\Pages\Pos;
use App\Filament\Resources\InvoiceResource;
use App\Models\Sales\Invoice;
use App\Services\CurrentContext;
use App\Support\Invoice\InvoiceBusiness;
use App\Support\Invoice\InvoiceDocument;
use App\Support\Invoice\InvoiceDocumentLine;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

/**
 * Renders a persisted invoice for printing, on either paper size.
 *
 * This is deliberately a plain controller rather than a Filament page: a print
 * has to arrive as a bare document with the right `@page` size and nothing else
 * on it, and it opens in its own tab. It renders the exact same two blades the
 * Master Control live preview renders, so what a landlord approves in the
 * preview is what comes out of the printer.
 */
class InvoicePrintController extends Controller
{
    /** The two supported stocks. Anything else is a 404, not a silent fallback. */
    public const PAPERS = ['a4', 'thermal80'];

    /**
     * Relations {@see InvoiceDocument::fromInvoice()} and
     * {@see InvoiceDocumentLine::fromItem()} walk. Loaded
     * up front because a receipt printed at a busy counter should not fire a
     * query per line. Public: {@see Pos::receiptDocument()}
     * reuses this list to render the same document inline in the POS
     * sale-complete modal, instead of a second request through this
     * controller.
     */
    public const RELATIONS = [
        'items.unit',
        'items.serial',
        'items.stockBatch',
        'items.bundle',
        'claims.transaction.method',
        'claims.transaction.status',
        'voucherRedemptions.voucher',
        'loyaltyTransactions.program',
        'customer.city',
        'deliveryAddress',
        'branch',
        'till',
        'rep',
        'orderStatus',
        'paymentStatus',
        'amendedFrom',
    ];

    public function __construct(private readonly CurrentContext $context) {}

    public function __invoke(Request $request, mixed $invoice, mixed $paper = null): View
    {
        // With path tenancy the route's {workspace} prefix parameter is the
        // FIRST route parameter: the framework's controller dispatcher assigns
        // method arguments positionally, so a typed `Invoice $invoice` here
        // receives the workspace slug and throws (docs/path-based-tenancy-plan.md
        // Phase 4). Arguments are untyped on purpose; the model is resolved
        // through the tenant-scoped query instead, which enforces exactly the
        // same owner check implicit binding did.
        $invoice = Invoice::query()->findOrFail((int) $request->route('invoice'));
        $paper = (string) ($request->route('paper') ?? 'a4');

        abort_unless(in_array($paper, self::PAPERS, true), 404);

        // `isms_invoices.print` for the invoice module's own print actions, or
        // `pos.checkout` for a cashier's own receipt — called through the one
        // place that owns that rule rather than restated here.
        abort_unless(Auth::check() && InvoiceResource::canPrint($invoice), 403);

        $invoice->load(self::RELATIONS);

        $business = InvoiceBusiness::forTenant($this->context->tenant());

        $doc = InvoiceDocument::fromInvoice(
            $invoice,
            $business,
            $this->context->currencySymbol(),
        );

        return view('invoices.print.document', [
            'doc' => $doc,
            'paper' => $paper,
            'invoiceKey' => $invoice->getKey(),
            // `?preview=1` embeds the document in an iframe (the POS receipt
            // modal) instead of opening the browser print dialog. This is
            // distinct from the Master Control letterhead preview flag also
            // named `preview` in the layouts below — it must NOT enable the
            // placeholder/blank-field letterhead markers, or a real receipt
            // with no email/website/registration number set would print
            // those fields blank instead of omitting them.
            'embed' => $request->boolean('preview'),
        ]);
    }
}
