import { useEffect, useId, useRef, useState } from 'react';
import { useHttp } from '@inertiajs/react';
import { Box, Clock, Info, Lock, X, ChevronDown, Pencil } from 'lucide-react';
import { cn } from '@/lib/utils';
import { FloatingField } from '@/Components/Proto/UI/FloatingField';
import { SearchableSelect } from '@/Components/Form/SearchableSelect';
import { ApprovalPill } from '@/Components/Proto/UI/ApprovalPill';
import { ApprovalCommentHistory } from '@/Components/MenuQuotations/Quotations/ApprovalCommentHistory';
import { formatIdr as fmtIdr, formatUsd as fmtUsd, formatGrouped } from '@/lib/currencyFormat';
import { useNumberFormat } from '@/Hooks/useNumberFormat';
import { ToProjectModal } from '@/Components/MenuProjects/ToProjectModal';
import { HistoryPopover } from '@/Components/MenuQuotations/QuotationDetailPage/HistoryPopover';

/**
 * Print-name toggle — a bare pencil, no caption (user 2026-08-05: "editnya diganti
 * gambar pencil aja biar lebi clean"). It used to be a checkbox chip reading "Edit
 * Print Name", ~120px of the select it sits inside; the label repeated what the
 * revealed field already says. `label` stays in the signature because it is what the
 * button announces to screen readers — the affordance is icon-only, not nameless.
 */
function PrintNameToggle({ checked, onChange, label, title }) {
    return (
        <button
            type="button"
            onClick={(e) => {
                e.stopPropagation();
                onChange({ target: { checked: !checked } });
            }}
            title={title}
            aria-label={label}
            aria-pressed={checked}
            className={cn(
                'inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md border transition-colors',
                checked
                    ? 'border-primary/40 bg-primary/10 text-primary'
                    : 'border-border/60 bg-secondary/60 text-muted-foreground hover:bg-secondary hover:text-foreground'
            )}
        >
            <Pencil aria-hidden="true" className="size-3" />
        </button>
    );
}

const EMPTY = {
    barang: null,
    principal: '',          // selected principal id — scopes the product dropdown
    principalPrintName: '', // print-facing principal name (defaults from principal, editable)
    usePrincipalPrintName: false, // if true, allow editing principalPrintName
    productPrintName: '',   // print-facing product name (defaults from product, editable)
    useProductPrintName: false,   // if true, allow editing productPrintName
    quantity: '',           // TOTAL qty = packaging content (packing weight) × packagingQty
    packagingQty: '',       // number of packages — UI-only; only the total persists
    unitPriceUsd: '',
    applicationId: '',
    packingId: '',          // selected packing id (packing table) — Weight auto-fills from it
    remark: '',
    remarkInternal: '',
    toProjectId: '',        // companyproject header id
    toProjectDetailId: '',  // companyprojectdetails id (picked together via ProjectModal)
    toProject: '',          // display label for the picker field + items table
    // Satuan dropdowns (ids from options.satuans). orderQtyUnit is the master: changing
    // it drives the two price units; the two price units sync with each other but never
    // back to orderQtyUnit. Only orderQtyUnit persists (as SatuanID) — the price units
    // are UI-only (no DB column).
    orderQtyUnit: '',
    unitPriceUsdUnit: '',
    unitPriceIdrUnit: '',
};

// Rebuild the modal's form state from a previously-emitted line (edit mode). `_`-prefixed
// fields on the line are UI-only carriers written by handleAccept for exactly this round-trip.
/**
 * "What did we write here last time" chip for a remark field. Sits absolutely over the
 * field's top border so it costs NO layout height — the two remark boxes were cut to 3/4
 * height on purpose, and an inline hint row would have taken half of that straight back.
 *
 * Deliberately does NOT prefill, unlike Unit Price. A stale price looks wrong and gets
 * corrected; stale prose reads as if it were written for this deal, and `Remarks` prints on
 * the customer's copy of the quotation. So the old text is offered and copying it is an
 * explicit click.
 */
function RemarkHistoryChip({ entries, title, onUse }) {
    if (!entries.length) return null;
    // NO z-index on the wrapper, on purpose. HistoryPopover renders its panel as
    // `fixed z-[100]`, but a z-index here makes this div a stacking context and confines
    // that 100 inside it — the SECOND chip (also positioned, later in the DOM) then painted
    // over the first chip's open popover and clipped its top row. Absolute positioning
    // alone already lifts the chip above the static textarea.
    return (
        <div className="absolute -top-2 right-2 bg-card px-1">
            <HistoryPopover count={entries.length} title={title} width={340}>
                {entries.map((e, i) => (
                    <div key={i} className="border-b border-border/50 px-3 py-2 last:border-b-0">
                        <div className="mb-1 flex items-center justify-between gap-2">
                            <span className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{e.date}</span>
                            <button
                                type="button"
                                onClick={() => onUse(e.text)}
                                className="rounded-full border border-border px-2 py-0.5 text-[10px] font-bold text-primary transition-colors hover:border-primary hover:bg-accent"
                            >
                                Pakai
                            </button>
                        </div>
                        <p className="m-0 text-[11.5px] leading-snug text-foreground">{e.text}</p>
                    </div>
                ))}
            </HistoryPopover>
        </div>
    );
}

const lineToForm = (line) => {
    if (!line) return { ...EMPTY };
    const str = (v) => (v === null || v === undefined || v === '' ? '' : String(v));
    const satuan = str(line.SatuanID);
    return {
        barang: line.BarangID != null
            ? { id: line.BarangID, name: line.ProductName, satuanId: line.SatuanID, satuanName: line._satuanName || '' }
            : null,
        principal: line._principalId ?? '',
        principalPrintName: line.PrincipalPrintName || '',
        usePrincipalPrintName: false,
        productPrintName: line.ProductName || '',
        useProductPrintName: false,
        quantity: line.OrderQuantity ? str(line.OrderQuantity) : '',
        packagingQty: '',   // derived from quantity ÷ packing weight at hydrate (needs options)
        unitPriceUsd: str(line.UnitPriceUSD),
        applicationId: str(line.ApplicationID),
        packingId: str(line.PackingID),
        remark: line.Remarks || '',
        remarkInternal: line.RemarkInternal || '',
        toProjectId: str(line.ToProjectID),
        toProjectDetailId: str(line.ToProjectDetailID),
        toProject: line.ToProject || '',
        orderQtyUnit: satuan,
        unitPriceUsdUnit: line._unitPriceUsdUnit ?? satuan,
        unitPriceIdrUnit: line._unitPriceIdrUnit ?? satuan,
    };
};

/**
 * Add/Edit Product modal for the Quotations Create form. Principal-first: choosing a
 * principal loads that principal's `barang` into the Product dropdown and surfaces
 * the principal's head-div approvers. Emits a quotationdetails-shaped line on Accept.
 * Pass `initial` (a previously-emitted line) to open in edit mode pre-filled with it.
 */
export function QuotationProductModal({ open, onClose, onAccept, options, applications = [], projects = [], usdRate = 0, companyId = '', isOrder = false, initial = null }) {
    const [form, setForm] = useState({ ...EMPTY });
    // Product-first picking: the full active-product list is fetched ONCE per modal-open
    // and cached; Principal then filters it client-side instead of re-fetching. Mirrors
    // the Sample Order Add Barang modal (2026-07-27).
    const [allProducts, setAllProducts] = useState([]);
    const [isProjectModalOpen, setIsProjectModalOpen] = useState(false);
    const [projectCleared, setProjectCleared] = useState(false);
    const titleId = useId();
    const list = useHttp({});
    const appLookup = useHttp({});
    const quoteLookup = useHttp({});
    // { lastQuote, history } for the CURRENT product+company, or null before the first answer.
    const [quoteInfo, setQuoteInfo] = useState(null);
    // Always-current product id, so a late / out-of-order application lookup can be discarded.
    const barangRef = useRef(form.barang?.id ?? null);
    barangRef.current = form.barang?.id ?? null;

    // Load every active product once per modal-open (add or edit) — no principal filter.
    useEffect(() => {
        if (!open) return;
        list.cancel();
        setAllProducts([]);
        setProjectCleared(false);
        list.get(route('quotations.products.byPrincipal'), {
            onSuccess: (data) => setAllProducts(Array.isArray(data) ? data : []),
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [open]);

    // Principal is a FILTER over the cached list, not a fetch trigger.
    const products = form.principal
        ? allProducts.filter((p) => String(p.principalId) === String(form.principal))
        : allProducts;

    // Auto-fill Application from the last time this product was quoted to this customer
    // (the lookup returns only a non-deleted application). Overwrite on every product
    // change: clear first, then fill from the lookup if found. The user can still override
    // the result afterwards via the Application dropdown.
    useEffect(() => {
        if (!open) return;
        appLookup.cancel();
        const barangId = form.barang?.id ?? null;
        // Clear on every product change — the "overwrite" guarantee.
        setForm((f) => (f.applicationId === '' ? f : { ...f, applicationId: '' }));
        if (!barangId || !companyId) return;
        appLookup.get(route('quotations.products.last-application', { barang: barangId, companyId }), {
            onSuccess: (data) => {
                // Discard a stale response for a product the user already left.
                if (barangRef.current !== barangId) return;
                const appId = data?.applicationId ?? null;
                // The lookup is already scoped to the company's division group (same as the
                // dropdown), so any id it returns is a valid, selectable option — apply it.
                if (appId) setForm((f) => ({ ...f, applicationId: String(appId) }));
            },
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [form.barang?.id, companyId, open]);

    // Previously quoted lines for this product + customer. Feeds three things: the Unit
    // Price prefill, the price hint under it, and the two remark popovers.
    useEffect(() => {
        if (!open) return;
        quoteLookup.cancel();
        setQuoteInfo(null);
        const barangId = form.barang?.id ?? null;
        if (!barangId || !companyId) return;
        quoteLookup.get(route('quotations.products.last-quote', { barang: barangId, companyId }), {
            onSuccess: (data) => {
                // Discard a stale response for a product the user already left — same guard
                // the Application lookup uses.
                if (barangRef.current !== barangId) return;
                setQuoteInfo(data ?? null);
                const usd = Number(data?.lastQuote?.unitUsd) || 0;
                if (usd <= 0) return;
                // Prefill ONLY into an empty box. Never overwrite a price the user typed, and
                // never the saved price of a line reopened for editing.
                setForm((f) => (String(f.unitPriceUsd).trim() !== '' ? f : { ...f, unitPriceUsd: String(usd) }));
            },
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [form.barang?.id, companyId, open]);

    // Hydrate the form each time the modal opens: from `initial` (edit mode) or a clean
    // slate (add mode). Keyed on `open` only — the parent always closes between rows, so a
    // fresh open re-runs this. The principal effect reloads the product list from the
    // hydrated form.principal; the auto-fill effect re-derives Application from the product.
    useEffect(() => {
        if (!open) return;
        list.cancel();
        appLookup.cancel();
        const f = lineToForm(initial);
        // Edit mode: rebuild the packaging count from the persisted TOTAL and the packing's
        // content weight (lineToForm can't — it has no access to the packing options).
        const w = Number((options.packings || []).find((p) => p.id === Number(f.packingId))?.weight) || 0;
        const total = parseFloat(f.quantity) || 0;
        if (w > 0 && total > 0) f.packagingQty = String(Math.round((total / w) * 100) / 100);
        setForm(f);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [open]);

    const update = (k, v) => setForm((f) => ({ ...f, [k]: v }));
    // Satuan sync. Order Qty is the master (drives all three); the two price units sync
    // with each other only. None of these propagate back up to Order Qty.
    const setUsdUnit = (v) => setForm((f) => ({ ...f, unitPriceUsdUnit: v, unitPriceIdrUnit: v }));
    const setIdrUnit = (v) => setForm((f) => ({ ...f, unitPriceIdrUnit: v, unitPriceUsdUnit: v }));
    // Order Qty is an order-only field (legacy disables it unless Is Order is on):
    // for non-orders the effective qty is 0, so subtotals show 0 like legacy.
    const qty = isOrder ? parseFloat(form.quantity) || 0 : 0;
    const priceUsd = parseFloat(form.unitPriceUsd) || 0;
    const round2 = (n) => Math.round(n * 100) / 100;
    const weightOf = (packingId) => Number((options.packings || []).find((p) => p.id === Number(packingId))?.weight) || 0;
    // Quantity model (user request 2026-08-05): the packing's weight IS the Packaging
    // Content; the user types the PACKAGING QTY (number of packages); the persisted
    // Quantity is their product. Typing the packaging qty recomputes the total.
    const pkgQtyField = useNumberFormat({
        value: form.packagingQty,
        onChange: (raw) => setForm((f) => {
            const w = weightOf(f.packingId);
            const pq = parseFloat(raw) || 0;
            return { ...f, packagingQty: raw, quantity: w > 0 && pq > 0 ? String(round2(w * pq)) : '' };
        }),
        decimals: 2,
    });
    // Total Qty is editable BOTH ways (user 2026-08-05): typing the total re-derives the
    // packaging qty from the current content, mirroring what a packing switch does.
    const totalQtyField = useNumberFormat({
        value: form.quantity,
        onChange: (raw) => setForm((f) => {
            const w = weightOf(f.packingId);
            const t = parseFloat(raw) || 0;
            return { ...f, quantity: raw, packagingQty: w > 0 && t > 0 ? String(round2(t / w)) : f.packagingQty };
        }),
        decimals: 2,
    });
    // decimals: 10 matches quotationdetails.UnitPriceUSD, which is decimal(20,10). At 2 the blur
    // handler SLICED the tail off any line carrying real sub-cent precision — 29 of 521 live
    // lines do (1407.4074074074, 17333.3333333333) — so merely opening such a line and clicking
    // out of the price box rewrote the price and saved it. Same precedent as Company Rebate,
    // whose entry fields use decimals: 5 to match their decimal(20,5) columns.
    const priceUsdField = useNumberFormat({ value: form.unitPriceUsd, onChange: (raw) => update('unitPriceUsd', raw), decimals: 10 });
    // The (hidden) order satuan, shown as a unit suffix behind the Total Qty box.
    const satuanName = (options.satuans || []).find((s) => String(s.id) === String(form.orderQtyUnit))?.name || '';
    // IDR is derived from USD × rate — never typed. Rounded to whole rupiah.
    const priceIdr = usdRate > 0 ? Math.round(priceUsd * usdRate) : 0;
    const subtotalUsd = qty * priceUsd;
    const subtotalIdr = qty * priceIdr;
    const totalIdr = subtotalIdr;
    const lastQuote = quoteInfo?.lastQuote ?? null;
    const historyRows = Array.isArray(quoteInfo?.history) ? quoteInfo.history : [];
    // A blank remark is not history — it would be a dated entry with nothing to copy.
    const remarkEntry = (key) => historyRows
        .filter((r) => String(r?.[key] ?? '').trim() !== '')
        .map((r) => ({ date: r.date, text: String(r[key]).trim() }));
    const internalEntries = remarkEntry('remarkInternal');
    const quotationEntries = remarkEntry('remarks');

    // Application is a CLIENT-ONLY legacy-parity requirement to ADD a line (createquotation.php:2027
    // gated add-row on idApplication != "" && != "0"). The server keeps items.*.ApplicationID
    // nullable so editing legacy details that hold NULL never 422s; this add-line gate just refuses
    // NEW lines without an Application, like legacy. It auto-fills from the last quote (effect above)
    // and can be picked manually from the Application select.
    const canAccept = form.barang && priceUsd > 0 && usdRate > 0 && !!form.packingId && !!form.applicationId && (!isOrder || qty > 0);

    // To Project pick — ToProjectModal groups the flat endpoint rows itself
    // (header step → detail step) and emits {id, detailId, title}; 'new' sentinels
    // mean Insert New Project / New Project Detail.
    const pickProject = (p) => {
        setProjectCleared(false);
        setForm((f) => {
            if (!p) return { ...f, toProjectId: '', toProjectDetailId: '', toProject: '' };
            const label = p.id === 'new'
                ? 'New Project'
                : `${p.title || 'Project '+p.id} / ${p.detailId === 'new' ? 'New Detail' : 'Detail '+p.detailId}`;
            return { ...f, toProjectId: String(p.id), toProjectDetailId: String(p.detailId), toProject: label };
        });
    };

    // The To Project pick is Application-scoped (ToProjectModal greys out details whose
    // Application differs), but Application can be changed AFTER a detail was picked. Drop a
    // pick that stopped matching — otherwise the invalid pair walks straight past the greyed
    // rows and only StoreQuotationRequest::after() catches it, as an error on a To Project
    // field that looks perfectly fine. Details with a NULL application never conflict.
    useEffect(() => {
        if (!form.toProjectDetailId || form.toProjectDetailId === 'new') return;

        const row = (projects || []).find((r) => r.detailId === Number(form.toProjectDetailId));
        const detailApp = row?.applicationId ?? null;
        if (detailApp === null) return;

        if (detailApp === (form.applicationId === '' ? null : Number(form.applicationId))) return;

        setForm((f) => ({ ...f, toProjectId: '', toProjectDetailId: '', toProject: '' }));
        setProjectCleared(true);
    }, [form.applicationId]); // eslint-disable-line react-hooks/exhaustive-deps

    const selectedPrincipal = (options.principals || []).find((p) => p.id === form.principal);
    const approver = selectedPrincipal?.approvers?.length ? selectedPrincipal.approvers.join(' / ') : '—';
    // Product-first (2026-07-27): subtext carries the principal name so the unfiltered
    // list is still cross-lookup browsable/searchable (was name-only when the list was
    // always pre-scoped to one principal).
    const productOptions = products.map((b) => ({
        id: b.id,
        name: b.name,
        subtext: (options.principals || []).find((p) => String(p.id) === String(b.principalId))?.name || '',
    }));
    // Keep the selected product visible even before its principal's list has loaded
    // (e.g. while editing an existing line) — otherwise the Product field would blank out.
    if (form.barang && !productOptions.some((o) => o.id === form.barang.id)) {
        productOptions.unshift({ id: form.barang.id, name: form.barang.name });
    }

    const reset = () => { list.cancel(); appLookup.cancel(); setForm({ ...EMPTY }); setAllProducts([]); };
    const handleClose = () => { reset(); onClose(); };

    // Principal is a FILTER over the cached product list, not a fetch trigger (2026-07-27).
    // Clearing it (val === '') always keeps the current product — it's still in the
    // widened list. Picking one keeps the product too UNLESS it belongs to a different
    // principal, in which case it's cleared to stay consistent with the narrowed list.
    const selectPrincipal = (val) => {
        const p = (options.principals || []).find((x) => x.id === val);
        const conflicts = val && form.barang && String(form.barang.principalId) !== String(val);
        setForm((f) => ({
            ...f,
            principal: val,
            principalPrintName: p?.name || f.principalPrintName,
            ...(conflicts ? { barang: null, productPrintName: '', orderQtyUnit: '', unitPriceUsdUnit: '', unitPriceIdrUnit: '' } : {}),
        }));
    };

    const selectProduct = (id) => {
        const b = allProducts.find((p) => p.id === id) || null;
        // Auto-fill Order Qty satuan from the product, cascading to both price units.
        // The product print name defaults to the chosen product's name (still editable).
        const unit = b?.satuanId != null ? String(b.satuanId) : '';
        setForm((f) => ({
            ...f,
            barang: b,
            // Auto-fill Principal from the product ONLY when it's still empty (2026-07-27) —
            // once a Principal is chosen it stays a manual filter.
            principal: f.principal || (b?.principalId ? b.principalId : f.principal),
            principalPrintName: f.principal
                ? f.principalPrintName
                : ((options.principals || []).find((p) => String(p.id) === String(b?.principalId))?.name || f.principalPrintName),
            productPrintName: b?.name || '',
            orderQtyUnit: unit,
            unitPriceUsdUnit: unit,
            unitPriceIdrUnit: unit,
        }));
    };

    // Changing the Unit (packing) re-seeds the Satuan selects from the packing's
    // SatuanID (legacy getsatuanpacking.php); clearing the packing clears them too.
    // The Order-Qty satuan follows only while it is editable (Is Order on) — while
    // locked it must not change underneath the user, mirroring legacy which gates
    // the InsertSatuanOrderQty sync on the same checkbox.
    const selectPacking = (id) => {
        const satuan = (options.packings || []).find((p) => p.id === Number(id))?.satuanId;
        const unit = id && satuan != null ? String(satuan) : '';
        setForm((f) => {
            // Packaging Content changed → the PACKAGING QTY adjusts to preserve the total
            // (user rule 2026-08-05). With no total yet, a typed packaging qty is kept and
            // the total derives from it instead.
            const w = weightOf(id);
            const total = parseFloat(f.quantity) || 0;
            const pq = parseFloat(f.packagingQty) || 0;
            let packagingQty = f.packagingQty;
            let quantity = f.quantity;
            if (w > 0 && total > 0) packagingQty = String(round2(total / w));
            else if (w > 0 && pq > 0) quantity = String(round2(w * pq));
            else if (!w) quantity = '';
            return {
                ...f,
                packingId: id,
                packagingQty,
                quantity,
                ...(isOrder ? { orderQtyUnit: unit } : {}),
                unitPriceUsdUnit: unit,
                unitPriceIdrUnit: unit,
            };
        });
    };

    const handleAccept = () => {
        if (!canAccept) return;
        const b = form.barang;
        const packing = options.packings.find((p) => p.id === Number(form.packingId));
        const app = applications.find((a) => a.id === Number(form.applicationId));
        // When the print-name checkbox is OFF, use the default (principal name / product name).
        const principalName = form.usePrincipalPrintName
            ? form.principalPrintName?.trim()
            : selectedPrincipal?.name || '';
        const productName = form.useProductPrintName
            ? form.productPrintName?.trim()
            : b.name;
        // SatuanID follows the (editable) Order Qty satuan dropdown, not the product default.
        const satuanId = form.orderQtyUnit ? Number(form.orderQtyUnit) : null;
        const satuan = (options.satuans || []).find((s) => s.id === satuanId);
        onAccept({
            _id: `line-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
            BarangID: b.id,
            PrincipalPrintName: principalName,
            ProductName: productName,
            // Legacy column semantics: the typed qty is OrderQuantity; QuantityPacking
            // (= packing weight) is derived server-side from PackingID.
            OrderQuantity: qty,
            SatuanID: satuanId,
            PackingID: form.packingId ? Number(form.packingId) : null,
            UnitPriceUSD: priceUsd,
            UnitPriceIDR: priceIdr,
            ApplicationID: form.applicationId ? Number(form.applicationId) : null,
            Remarks: form.remark || '',
            RemarkInternal: form.remarkInternal || '',
            ToProjectID: form.toProjectId ? (form.toProjectId === 'new' ? 'new' : Number(form.toProjectId)) : null,
            ToProjectDetailID: form.toProjectDetailId ? (form.toProjectDetailId === 'new' ? 'new' : Number(form.toProjectDetailId)) : null,
            ToProject: form.toProject || '',
            _satuanName: satuan?.name || '',
            _packName: packing?.name || '',
            _applicationName: app?.name || '',
            // UI-only round-trip carriers (backend ignores `_`-prefixed keys): let an edit
            // rehydrate the principal + the two (unpersisted) price-unit satuans exactly.
            _principalId: form.principal,
            _unitPriceUsdUnit: form.unitPriceUsdUnit,
            _unitPriceIdrUnit: form.unitPriceIdrUnit,
        });
        handleClose();
    };

    return (
        <div
            className={cn(
                'fixed inset-0 z-50 items-center justify-center overflow-y-auto p-6',
                open ? 'flex' : 'hidden',
                // While the project picker is up this overlay is just a mount point: it drops
                // its dim + blur so the picker's own backdrop is the only one on screen.
                isProjectModalOpen ? 'pointer-events-none' : 'bg-foreground/40 backdrop-blur-[6px]',
            )}
            aria-hidden={!open}
            onClick={() => { if (!isProjectModalOpen) handleClose(); }}
        >
            <div
                className={cn(
                    'relative flex max-h-[calc(100vh-48px)] w-[min(760px,100%)] flex-col overflow-hidden rounded-2xl bg-card shadow-xl',
                    // Step aside for the To Project picker; Cancel/select brings it right back
                    // (user 2026-08-06). `hidden`, not unmounted — every field keeps its value.
                    isProjectModalOpen && 'hidden',
                )}
                role="dialog"
                aria-modal="true"
                aria-labelledby={titleId}
                onClick={(e) => e.stopPropagation()}
            >
                <button className="absolute right-3.5 top-3.5 z-2 inline-grid size-7.5 place-items-center rounded-full bg-transparent text-muted-foreground transition-colors hover:bg-secondary hover:text-card-foreground" type="button" aria-label="Close" onClick={handleClose}>
                    <X aria-hidden="true" className="size-4.5" />
                </button>

                <header className="flex items-center gap-3.5 border-b border-border bg-card p-[18px_24px] text-card-foreground">
                    <span className="inline-flex size-10 shrink-0 items-center justify-center rounded-xl bg-accent text-primary" aria-hidden="true">
                        <Box className="size-4.5" strokeWidth={1.8} />
                    </span>
                    <h2 id={titleId} className="m-0 flex-1 text-lg font-bold tracking-[-0.01em] text-card-foreground">{initial ? 'Edit Product' : 'Add Product'}</h2>
                </header>

                <div className="flex-1 overflow-y-auto p-[26px_32px]">
                    {/* Two columns, THREE sections. Pricing sits under Product Details in the
                        left column on purpose: Subtotal = Order Qty x Unit Price, so the two
                        numbers being multiplied belong in the same column, one above the other.
                        Application / To Project / the two remarks are neither product identity
                        nor price — they get the right column to themselves. */}
                    <div className="flex flex-col gap-7">
                        <section className="relative min-w-0 p-0">
                            <header className="mb-3.5 flex min-h-8.5 items-center gap-2.5 border-b border-border pb-2.5">
                                <h2 className="m-0 text-[11px] font-bold uppercase tracking-[0.08em] text-primary">Product Details</h2>
                            </header>

                            <div className="grid gap-4" data-tut="product-pick">
                              {/* Principal + Product share a row (user 2026-08-06: full-width
                                  selects read "kepanjangan" in the one-column layout). */}
                              <div className="grid gap-4 sm:grid-cols-2">
                                {/* Each select owns its print-name field and the field opens
                                    STRAIGHT BELOW it (user 2026-08-05), never beside it — so
                                    Principal and Product keep their row whatever is toggled, and
                                    the print name reads as belonging to the select above it.
                                    content-start: the shorter column must not stretch to match. */}
                                <div className="grid content-start gap-3">
                                    <SearchableSelect
                                        label="Principal"
                                        placeholder="Select principal"
                                        searchPlaceholder="Search principal name"
                                        options={(options.principals || []).map((p) => ({ id: p.id, name: p.name }))}
                                        value={form.principal}
                                        onChange={selectPrincipal}
                                        suffix={
                                            <PrintNameToggle
                                                checked={form.usePrincipalPrintName}
                                                onChange={(e) => update('usePrincipalPrintName', e.target.checked)}
                                                label="Edit Principal Print Name"
                                                title="Edit Principal Print Name"
                                            />
                                        }
                                    />
                                    {form.usePrincipalPrintName && (
                                        <FloatingField
                                            label="Principal Print Name"
                                            type="text"
                                            value={form.principalPrintName}
                                            onChange={(e) => update('principalPrintName', e.target.value)}
                                            disabled={!form.principal}
                                        />
                                    )}
                                </div>

                                <div className="grid content-start gap-3">
                                <SearchableSelect
                                    label="Product *"
                                    placeholder="Select product"
                                    searchPlaceholder="Search product name"
                                    emptyText={list.processing ? 'Memuat produk…' : 'No products found'}
                                    options={productOptions}
                                    value={form.barang?.id ?? ''}
                                    onChange={selectProduct}
                                    limit={100}
                                    suffix={
                                        <PrintNameToggle
                                            checked={form.useProductPrintName}
                                            onChange={(e) => update('useProductPrintName', e.target.checked)}
                                            label="Edit Product Print Name"
                                            title="Edit Product Print Name"
                                        />
                                    }
                                />
                                {form.useProductPrintName && (
                                    <FloatingField
                                        label="Product Print Name"
                                        type="text"
                                        value={form.productPrintName}
                                        onChange={(e) => update('productPrintName', e.target.value)}
                                        disabled={!form.barang}
                                    />
                                )}
                                </div>
                              </div>

                                {/* ONE row (user 2026-08-05): Packaging Content (the picker — its
                                    option text names the content, "12.5 kg/pail") · Total Pack
                                    (typed) = Total Qty (content × packs, persists as OrderQuantity).
                                    The middle field was labelled "Packaging Qty" until 2026-08-24;
                                    the state key `packagingQty` keeps the old name deliberately —
                                    it is UI-only and renaming it would touch the hydrate/derive
                                    paths for a caption change.
                                    No visible Satuan select — it seeds silently from the
                                    product/packing and persists as SatuanID. Content changes
                                    re-derive the packaging qty so the total holds (selectPacking);
                                    qty stays order-only like the old Order Qty (legacy gate). */}
                                <div className="grid grid-cols-3 items-start gap-3" data-tut="product-qty">
                                    <FloatingField as="select" size="sm" label="Packaging Content *" value={form.packingId} onChange={(e) => selectPacking(e.target.value)}>
                                        <option value="">Select packing</option>
                                        {options.packings.map((p) => <option key={p.id} value={p.id}>{p.name}</option>)}
                                    </FloatingField>
                                    <FloatingField
                                        size="sm" label={isOrder ? 'Total Pack *' : 'Total Pack'}
                                        disabled={!isOrder} inputMode="decimal"
                                        {...pkgQtyField} value={isOrder ? pkgQtyField.value : ''}
                                    />
                                    <FloatingField
                                        size="sm" label="Total Qty"
                                        disabled={!isOrder} inputMode="decimal"
                                        {...totalQtyField} value={isOrder ? totalQtyField.value : ''}
                                    />
                                </div>
                                {satuanName && (
                                    /* Unit of the qty figures — the satuan seeded from product/packing. */
                                    <p className="m-0 -mt-1 text-right text-[10.5px] font-bold text-muted-foreground">Satuan: {satuanName}</p>
                                )}
                                {!isOrder && (
                                    <p className="m-0 flex items-center gap-2 rounded-md bg-secondary/60 px-2.5 py-1.5 text-[11px] font-medium leading-snug text-muted-foreground">
                                        <Lock aria-hidden="true" className="size-3 shrink-0" />
                                        <span>Total Pack aktif saat <strong className="font-semibold text-foreground">Is Order</strong> menyala</span>
                                    </p>
                                )}
                            </div>
                        </section>

                        {/* Pricing Summary — still the left column, directly under Order Qty */}
                        <section className="relative min-w-0 p-0">
                            <header className="mb-3.5 flex min-h-8.5 items-center gap-2.5 border-b border-border pb-2.5">
                                <h2 className="m-0 text-[11px] font-bold uppercase tracking-[0.08em] text-primary">Pricing Summary</h2>
                                {/* The rate is SET on the Create page (Product Items header) — here it is
                                    display-only (user 2026-08-05: "nampil tapi uda gabisa diedit").
                                    When it is still 0 this says so OUT LOUD: every IDR figure below
                                    would read Rp 0 and Add Product stays disabled (canAccept needs a
                                    rate), which otherwise looks like the modal is simply broken. */}
                                {usdRate > 0 ? (
                                    <span className="ml-auto rounded-full bg-accent px-2.5 py-1 text-[10.5px] font-bold text-primary">
                                        Rate USD → IDR <span className="tabular-nums">{formatGrouped(usdRate, { decimals: 0 })}</span>
                                    </span>
                                ) : (
                                    <span className="ml-auto rounded-full bg-secondary px-2.5 py-1 text-[10.5px] font-bold text-muted-foreground">
                                        Rate USD → IDR belum diisi
                                    </span>
                                )}
                            </header>

                            {/* Prices at the left, the TOTAL (IDR) box standing beside them at the
                                right — the mockup's shape (user 2026-08-06). Every input the old
                                two-column layout had is still here; only the arrangement moved. */}
                            <div className="grid items-start gap-4 sm:grid-cols-[minmax(0,1fr)_210px]">
                              <div className="grid gap-3.5">
                                {/* USD: unit price · satuan · subtotal on ONE row (subtotal inline).
                                    Satuan column deliberately NARROW (user 2026-08-05) — it holds "/ Kg". */}
                                <div className="grid grid-cols-[1fr_110px_minmax(92px,auto)] items-center gap-2.5" data-tut="product-price">
                                    <FloatingField label="Unit Price $ *" {...priceUsdField} />
                                    <FloatingField as="select" label="Satuan" value={form.unitPriceUsdUnit} onChange={(e) => setUsdUnit(e.target.value)}>
                                        <option value="">Select Satuan</option>
                                        {(options.satuans || []).map((s) => <option key={s.id} value={s.id}>/ {s.name}</option>)}
                                    </FloatingField>
                                    <div className="text-right">
                                        <span className="block text-[10px] font-extrabold uppercase tracking-wide text-muted-foreground">Subtotal $</span>
                                        <span className="font-bold tabular-nums text-card-foreground">{fmtUsd(subtotalUsd)}</span>
                                    </div>
                                </div>
                                {/* Stays visible AFTER the user edits the box — the prefilled
                                    number alone cannot tell you what the old price was once it
                                    has been typed over. */}
                                <div className="-mt-2">
                                    {lastQuote && Number(lastQuote.unitUsd) > 0 ? (
                                        <p className="m-0 text-[10.5px] font-medium text-muted-foreground">
                                            Terakhir di-quote ke customer ini:{' '}
                                            <strong className="font-bold text-foreground">{fmtUsd(Number(lastQuote.unitUsd))}</strong>
                                            {lastQuote.satuanName ? ` / ${lastQuote.satuanName}` : ''}
                                            {lastQuote.date ? ` · ${lastQuote.date}` : ''}
                                        </p>
                                    ) : form.barang && companyId ? (
                                        <p className="m-0 text-[10.5px] font-medium text-muted-foreground/70">
                                            Belum pernah di-quote ke customer ini.
                                        </p>
                                    ) : null}
                                </div>

                                {/* IDR: unit price (read-only, USD × rate) · satuan · subtotal on ONE row */}
                                <div className="grid grid-cols-[1fr_110px_minmax(92px,auto)] items-center gap-2.5">
                                    <FloatingField label="Unit Price (IDR) *" type="text" readOnly value={priceIdr ? formatGrouped(priceIdr, { decimals: 2 }) : ''} />
                                    <FloatingField as="select" label="Satuan" value={form.unitPriceIdrUnit} onChange={(e) => setIdrUnit(e.target.value)}>
                                        <option value="">Select Satuan</option>
                                        {(options.satuans || []).map((s) => <option key={s.id} value={s.id}>/ {s.name}</option>)}
                                    </FloatingField>
                                    <div className="text-right">
                                        <span className="block text-[10px] font-extrabold uppercase tracking-wide text-muted-foreground">Subtotal (IDR)</span>
                                        <span className="font-bold tabular-nums text-card-foreground">{fmtIdr(subtotalIdr)}</span>
                                    </div>
                                </div>

                              </div>

                              {/* Total (IDR) — its own block at the right, like the mockup. */}
                              <div className="rounded-xl border border-primary bg-accent/50 px-4 py-3">
                                  <span className="block text-[11px] font-semibold uppercase text-primary">Total (IDR)</span>
                                  <strong className="mt-0.5 block text-[20px] font-extrabold leading-tight text-primary">{fmtIdr(totalIdr)}</strong>
                                  <p className="mt-1.5 flex items-start gap-1 text-[10px] leading-snug text-primary/70">
                                      <Info aria-hidden="true" className="mt-px size-3 shrink-0" />
                                      Auto calculated based on above details
                                  </p>
                              </div>
                            </div>
                        </section>

                        {/* Application & Remarks — third stacked section (mockup order). */}
                        <section className="relative flex min-w-0 flex-col p-0">
                            <header className="mb-3.5 flex min-h-8.5 items-center gap-2.5 border-b border-border pb-2.5">
                                <h2 className="m-0 text-[11px] font-bold uppercase tracking-[0.08em] text-primary">Application &amp; Remarks</h2>
                            </header>

                            <div className="flex flex-1 flex-col gap-4">
                              {/* Application + To Project share one row (mockup). */}
                              <div className="grid gap-4 sm:grid-cols-2">
                                <div data-tut="product-application"><FloatingField as="select" label="Application *" value={form.applicationId} onChange={(e) => update('applicationId', e.target.value)}>
                                    <option value="">Select application</option>
                                    {applications.map((a) => <option key={a.id} value={a.id}>{a.name}</option>)}
                                </FloatingField></div>

                                {/* To Project */}
                                <div data-tut="product-project">
                                    <label className="relative block cursor-pointer">
                                        <input type="text" placeholder=" " readOnly onClick={() => setIsProjectModalOpen(true)} value={form.toProject || ""} className="peer h-11 w-full cursor-pointer rounded-lg border border-input bg-card pl-3 pr-9 text-[12px] font-medium text-foreground outline-none transition-colors placeholder:text-transparent focus:border-primary focus:ring-1 focus:ring-primary"/>
                                        <span className="pointer-events-none absolute z-1 top-0 left-1.5 -translate-y-1/2 bg-card px-1 text-[9px] font-semibold text-muted-foreground">To Project</span>
                                        <span className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 inline-grid place-items-center text-muted-foreground" aria-hidden="true">
                                            <ChevronDown className="size-3" strokeWidth={2.4} />
                                        </span>
                                    </label>
                                    {projectCleared && (
                                        <p className="m-0 mt-1.5 text-[11px] font-medium text-muted-foreground">
                                            Pilihan To Project dihapus karena Application-nya berubah. Pilih ulang bila perlu.
                                        </p>
                                    )}
                                </div>
                              </div>

                                {/* Remarks run FULL width under that row (mockup). Shorter now that
                                    they no longer have to absorb a tall neighbouring column.
                                    `data-tut` stays on the first one — it is the tutorial's anchor
                                    for the remarks step, and the step has nothing to point at
                                    without it. */}
                                <div className="relative">
                                    <FloatingField data-tut="product-remarks" as="textarea" label="Project / Remark Internal" maxLength={500} value={form.remarkInternal} onChange={(e) => update('remarkInternal', e.target.value)} className="[&_textarea]:!min-h-[69px] [&_textarea]:!pt-4" />
                                    <RemarkHistoryChip entries={internalEntries} title="Remark Internal sebelumnya" onUse={(t) => update('remarkInternal', t)} />
                                </div>

                                <div className="relative">
                                    <FloatingField as="textarea" label="Remark in Quotation" maxLength={500} value={form.remark} onChange={(e) => update('remark', e.target.value)} className="[&_textarea]:!min-h-[69px] [&_textarea]:!pt-4" />
                                    <RemarkHistoryChip entries={quotationEntries} title="Remark in Quotation sebelumnya" onUse={(t) => update('remark', t)} />
                                </div>
                            </div>
                        </section>
                    </div>

                    {/* Last Quotation — PM + SM approval-comment history per product+company */}
                    <div className="mt-5 rounded-xl border border-border bg-secondary/40 p-4">
                        <div className="mb-3 flex items-center gap-2">
                            <Clock aria-hidden="true" className="size-4 text-muted-foreground" strokeWidth={1.8} />
                            <h3 className="text-[11px] font-bold uppercase tracking-wide text-card-foreground">Last Quotation</h3>
                        </div>
                        <div className="grid grid-cols-2 gap-3">
                            <ApprovalCommentHistory stage="pm" open={open} barangId={form.barang?.id ?? null} companyId={companyId} />
                            <ApprovalCommentHistory stage="sm" open={open} barangId={form.barang?.id ?? null} companyId={companyId} />
                        </div>
                    </div>
                </div>

                {/* Cancel + primary at bottom-RIGHT, matching the Add Product mockup (user request). */}
                <footer className="flex items-center justify-end gap-2.5 border-t border-border bg-secondary/40 p-[14px_24px]">
                    <button className="inline-flex h-9 items-center justify-center gap-1.5 rounded-lg border border-input bg-card px-4 text-xs font-bold text-foreground transition-colors hover:border-primary hover:text-primary" type="button" onClick={handleClose}>
                        Cancel
                    </button>
                    <button
                        className={cn('inline-flex h-9 items-center justify-center gap-1.5 rounded-lg px-4 text-xs font-bold shadow-sm transition-[filter] hover:brightness-105', canAccept ? 'bg-linear-to-br from-violet-500 to-primary text-white' : 'cursor-not-allowed bg-muted-foreground/40 text-primary-foreground')}
                        type="button"
                        onClick={handleAccept}
                        disabled={!canAccept}
                        data-tut="product-accept"
                    >
                        {initial ? 'Save' : 'Add Product'}
                    </button>
                </footer>
            </div>
            <div className="pointer-events-auto"><ToProjectModal open={isProjectModalOpen} onClose={() => setIsProjectModalOpen(false)} onSelect={pickProject} selected={form.toProjectId ? (form.toProjectId === 'new' ? 'new' : Number(form.toProjectId)) : null} projects={projects} lineApplicationId={form.applicationId} /></div>
        </div>
    );
}
