<?php

namespace App\Filament\Forms\Components;

use App\Models\Inventory\BarcodeLabel;
use App\Services\CurrentContext;
use App\Services\VatSettings;
use Filament\Forms\Components\ViewField;

/**
 * Product grid + cart for the Sales Order form: a POS-style picker for catalog
 * products, with a **custom item** builder sitting at the head of the grid.
 *
 * The component's state IS the `items` form state — a list of lines shaped for
 * SalesOrderService::create()/update():
 *
 *   catalog line  { product_id, batch_id, quantity, unit_price, vat_rate }
 *   custom line   { is_custom: true, product_name, custom_description,
 *                   custom_specification, quantity, unit_price, vat_rate }
 *
 * Catalog lines carry the lot they were priced against (`batch_id`): price is
 * a property of the receipt lot, so the order form quotes from the chosen lot
 * and SalesOrderService re-validates/reprices from it at quote time and again
 * at conversion.
 *
 * Bound with a deferred entangle, so picking products costs no round-trips and
 * the normal Filament validation/submit pipeline receives the finished list.
 *
 * **The catalog is NOT rendered into this field.** Filament evaluates a field's
 * viewData on every render and the whole form re-renders on every Livewire
 * round trip, so building the branch-priced catalog here meant paying for it
 * on interactions that had nothing to do with the grid — opening the customer
 * dropdown, adding a payment row, typing in the note — which is what made the
 * form feel like it was loading constantly. The view now renders an empty
 * shell and asks for the catalog once per branch over a renderless call (see
 * {@see \App\Filament\Resources\Concerns\InteractsWithSalesOrderCatalog}), so
 * a form render costs nothing catalog-sized no matter how large the catalog
 * is.
 *
 * Stock is shown but never enforced here: a sales order is a promise about the
 * future, so ordering more than is currently on the shelf is the ordinary case
 * (it gets made or bought before the deadline). The stock check happens once,
 * for real, when the order is converted into an invoice. Custom lines have no
 * stock at all — they are made to order.
 */
class SalesOrderProductGrid extends ViewField
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->view('filament.forms.sales-order-product-grid');
        $this->columnSpanFull();
        $this->required();

        $this->viewData(function (): array {
            // The order's branch: the form's own branch_id once one is picked,
            // otherwise the ambient context's branch — what the grid prices
            // against before the operator has touched the field. Only the id
            // is rendered; the client fetches the catalog for it on boot.
            $formBranchId = $this->formBranchId();

            return [
                'initialBranchId' => $formBranchId ?? app(CurrentContext::class)->branchId(),
                'vatEnabled' => app(VatSettings::class)->enabled(),
                // The namespace shop-printed labels live in — the grid tells
                // one from a product SKU/barcode by it, so an ordinary scan
                // never costs a round-trip. See the view's scanPrintedLabel().
                'labelPrefix' => BarcodeLabel::CODE_PREFIX,
            ];
        });
    }

    /**
     * The branch picked on the form, or null while the field is still blank —
     * in which case the grid falls back to the current context's branch (see
     * setUp()).
     */
    protected function formBranchId(): ?int
    {
        $formBranchId = (int) ($this->getContainer()->getRawState()['branch_id'] ?? 0);

        return $formBranchId ?: null;
    }
}
