import React, { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
import {
    Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
} from "@/components/ui/select";
import { Plus, Trash2, ArrowLeft, Search, Sparkles, Minimize2, Truck, FileText, Image as ImageIcon, Paperclip, MapPin, Printer, Share2, MessageCircle, Send } from "lucide-react";
import {
    Popover, PopoverContent, PopoverTrigger,
} from "@/components/ui/popover";
import { Badge } from "@/components/ui/badge";
import { toast } from "sonner";
import { api, formatApiError } from "@/lib/api";
import { offerWaShortcut, INVOICE_TYPE_TO_EVENT } from "@/lib/waShortcut";
import { useCompany } from "@/context/CompanyContext";
import { useI18n } from "@/context/I18nContext";
import { formatINR } from "@/lib/format";
import { useMinimizable } from "@/context/WindowManagerContext";
import { BillScanUpload } from "@/components/BillScanUpload";
import { Row, PartyPicker, ItemPicker } from "@/pages/invoice/Pickers";
import { QuickAddPartyModal } from "@/pages/invoice/QuickAddPartyModal";
import { QuickAddItemModal } from "@/pages/invoice/QuickAddItemModal";
import { InvoicePrefixPicker } from "@/pages/invoice/InvoicePrefixPicker";
import { OldBillSearch } from "@/pages/invoice/OldBillSearch";
import { VoiceToInvoice } from "@/pages/invoice/VoiceToInvoice";

const TYPE_META = {
    sale: { title: "New Sale Invoice", label: "Customer", partyType: "customer", listPath: "/sales" },
    purchase: { title: "New Purchase Bill", label: "Vendor", partyType: "vendor", listPath: "/purchases" },
    quotation: { title: "New Quotation", label: "Customer", partyType: "customer", listPath: "/quotations" },
    challan: { title: "New Delivery Challan", label: "Customer", partyType: "customer", listPath: "/sales" },
    sale_order: { title: "New Sale Order", label: "Customer", partyType: "customer", listPath: "/sale-orders" },
    proforma: { title: "New Proforma Invoice", label: "Customer", partyType: "customer", listPath: "/proforma" },
    credit_note: { title: "New Credit Note", label: "Customer", partyType: "customer", listPath: "/credit-notes" },
    debit_note: { title: "New Debit Note", label: "Vendor", partyType: "vendor", listPath: "/debit-notes" },
};

const blankLine = () => ({
    _uid: typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `ln-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
    item_id: null, name: "", hsn: "", qty: 1, unit: "PCS", rate: 0, discount: 0, gst_rate: 18,
    // Extended fields (Vyapar/Tally style)
    description: "", colour: "", size: "", brand: "", batch_no: "", serial_no: "",
    exp_date: "", mrp: 0, free_qty: 0,
    _expanded: false,
});

// v11 — Vyapar-style tax dropdown. Keeps `gst_rate` as a numeric value in the
// data layer (so PDF / reports keep working) but the UI is a labelled picker.
// Auto-shows IGST labels for inter-state, GST/CGST+SGST labels otherwise.
const TAX_RATES = [0, 0.1, 0.25, 1, 1.5, 3, 5, 6, 12, 18, 28];
function taxLabel(value, interstate) {
    const v = Number(value);
    if (Number.isNaN(v)) return "None";
    if (v === 0) return "None (0%)";
    const stripped = (v % 1 === 0) ? v.toFixed(0) : v.toString();
    return `${interstate ? "IGST" : "GST"} @ ${stripped}%`;
}

function ItemTaxSelect({ value, onChange, interstate, testid }) {
    const current = (value === undefined || value === null || value === "") ? 0 : Number(value);
    return (
        <Select value={String(current)} onValueChange={(v) => onChange(parseFloat(v))}>
            <SelectTrigger className="h-8 w-[110px] text-xs px-2" data-testid={testid}>
                <SelectValue>{taxLabel(current, interstate)}</SelectValue>
            </SelectTrigger>
            <SelectContent className="max-h-[300px]">
                {TAX_RATES.map((r) => (
                    <SelectItem key={r} value={String(r)} className="text-xs">{taxLabel(r, interstate)}</SelectItem>
                ))}
            </SelectContent>
        </Select>
    );
}

export default function NewInvoice({ mode = "sale" }) {
    const meta = TYPE_META[mode] || TYPE_META.sale;
    const { activeId, active } = useCompany();
    const { t } = useI18n();
    const navigate = useNavigate();
    const { id: editId } = useParams();    // present only when route is /sales/:id/edit, etc.
    const isEdit = !!editId;

    const [parties, setParties] = useState([]);
    const [items, setItems] = useState([]);
    const [party, setParty] = useState(null);
    const [walkInName, setWalkInName] = useState("");
    const [lines, setLines] = useState([blankLine()]);
    const [extraDiscount, setExtraDiscount] = useState(0);
    const [roundOff, setRoundOff] = useState(0);
    const [taxInclusive, setTaxInclusive] = useState(false);
    const [paymentReceived, setPaymentReceived] = useState(0);
    const [paymentMode, setPaymentMode] = useState("Cash");
    const [invoiceDate, setInvoiceDate] = useState(() => new Date().toISOString().slice(0, 10));
    const [notes, setNotes] = useState("");
    const [busy, setBusy] = useState(false);
    // Logistics & document copy controls (Vyapar/Tally-style)
    const [transportName, setTransportName] = useState("");
    const [vehicleNo, setVehicleNo] = useState("");
    const [deliveryLocation, setDeliveryLocation] = useState("");
    const [deliveryCharge, setDeliveryCharge] = useState(0);
    const [packagingCharge, setPackagingCharge] = useState(0);
    const [paymentTerms, setPaymentTerms] = useState("credit");  // 'credit' or 'cash'
    const [billingAddress, setBillingAddress] = useState("");
    const [shippingAddress, setShippingAddress] = useState("");
    const [billingPhone, setBillingPhone] = useState("");
    const [billingName, setBillingName] = useState("");
    // v11: Show extra columns by default (Vyapar-parity — Batch/Serial/Colour/Size/MRP/Free visible upfront)
    const [showExtraCols, setShowExtraCols] = useState(true);
    const [copyType, setCopyType] = useState("ORIGINAL");
    const [termsText, setTermsText] = useState("");
    // ---- v10.2: Vyapar-parity advanced fields ----
    const [description, setDescription] = useState("");                       // dedicated descriptive notes
    const [adjustment, setAdjustment] = useState(0);                          // +/- adjustment, separate from round-off
    const [roundOffMode, setRoundOffMode] = useState("nearest_rupee");        // 'nearest_rupee' | 'nearest_50p' | 'manual'
    const [autoRoundOff, setAutoRoundOff] = useState(true);
    const [charges, setCharges] = useState({                                  // additional charge bucket
        loading: 0, unloading: 0, freight: 0, insurance: 0, labour: 0, other: 0,
    });
    const [attachments, setAttachments] = useState([]);                       // [{name, data_url, size}]
    const [termsTemplates, setTermsTemplates] = useState([]);                 // loaded from /api/terms-templates
    // Mixed payment support
    const [mixedPayments, setMixedPayments] = useState([]);                   // [{mode: 'Cash'|'UPI'|..., amount, ref}]
    const [showMixedPayment, setShowMixedPayment] = useState(false);
    // v11 — Tax mode toggle (GST / Non-GST) + upfront invoice number preview
    const [taxMode, setTaxMode] = useState("GST");                            // "GST" | "NON_GST"
    const [previewInvoiceNo, setPreviewInvoiceNo] = useState("");             // upfront display, not consumed
    const [financialYear, setFinancialYear] = useState("");
    const [selectedPrefixId, setSelectedPrefixId] = useState(null);            // v12: chosen prefix series id
    const [prefixSeriesLabel, setPrefixSeriesLabel] = useState("");           // human-readable label (e.g. "RM/{yy}/")
    // v12 — Invoice Number Custom Mode (Vyapar parity)
    const [invoiceNoMode, setInvoiceNoMode] = useState("auto");               // "auto" | "custom"
    const [customInvoiceNo, setCustomInvoiceNo] = useState("");               // when custom, user-typed override

    useEffect(() => {
        if (!activeId) return;
        // Hydrate from local cache instantly (offline-first read)
        import("@/lib/localdb").then(async ({ getCached }) => {
            const [pCache, iCache] = await Promise.all([
                getCached("parties", (p) => p.company_id === activeId && (!meta.partyType || p.type === meta.partyType)),
                getCached("items", (i) => i.company_id === activeId),
            ]);
            if (pCache.length) setParties(pCache);
            if (iCache.length) setItems(iCache);
        });
        // Then refresh from network and update cache
        api.get("/parties", { params: { company_id: activeId, type: meta.partyType } })
            .then(async (r) => {
                setParties(r.data);
                const { snapshotTable } = await import("@/lib/localdb");
                // We only update cache for the rows we just fetched (scoped by type)
                snapshotTable("parties", r.data.map((p) => ({ ...p, company_id: activeId })));
            })
            .catch(() => { /* offline fallback already in place */ });
        api.get("/items", { params: { company_id: activeId } })
            .then(async (r) => {
                setItems(r.data);
                const { snapshotTable } = await import("@/lib/localdb");
                snapshotTable("items", r.data.map((i) => ({ ...i, company_id: activeId })));
            })
            .catch(() => { });
    }, [activeId, meta.partyType]);

    // ---- Scanner → Invoice handoff (Phase A v2) ----
    // When the Floating AI Camera fires "add_to_sale" / "add_to_purchase",
    // it writes the identified item to sessionStorage and navigates here.
    // We pick it up exactly once, push as a new line, then clear the key.
    useEffect(() => {
        try {
            const raw = sessionStorage.getItem("rbs_pending_scan_line_v1");
            if (!raw) return;
            const pending = JSON.parse(raw);
            sessionStorage.removeItem("rbs_pending_scan_line_v1");
            if (!pending || typeof pending !== "object") return;
            // Only consume if mode matches (sale ↔ /sales/new, purchase ↔ /purchase/new)
            if (pending.target_mode && pending.target_mode !== mode) return;
            const newLine = {
                ...blankLine(),
                item_id: pending.item_id || null,
                name: pending.name || "",
                hsn: pending.hsn || "",
                unit: pending.unit || "PCS",
                qty: Number(pending.qty) || 1,
                rate: Number(pending.rate) || 0,
                gst_rate: pending.gst_rate ?? 18,
                description: pending.brand ? `Brand: ${pending.brand}` : "",
            };
            // Replace the first blank line if it's empty, else append
            setLines((ls) => {
                const firstBlank = ls.length === 1 && !ls[0].name && !ls[0].item_id;
                return firstBlank ? [newLine] : [...ls, newLine];
            });
            toast.success(`Scanned: ${newLine.name}`);
        } catch (e) {
            console.debug("scan handoff parse failed", e?.message);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    // ---- v10.2: Load Terms & Conditions templates for the current mode ----
    useEffect(() => {
        const cat = (mode || "sale").replace("_", "");
        // Map invoice type → terms category
        const catMap = { sale: "sales", purchase: "purchase", quotation: "quotation" };
        const wantedCat = catMap[mode] || "sales";
        api.get(`/terms-templates?category=${wantedCat}`)
            .then(({ data }) => {
                setTermsTemplates(data || []);
                // Auto-apply default template ONLY when creating a fresh invoice
                if (!isEdit && (data || []).length && !termsText) {
                    const def = (data || []).find((t) => t.is_default) || data[0];
                    if (def?.body) setTermsText(def.body);
                }
            })
            .catch(() => { /* templates optional */ });
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [mode]);

    // ---- EDIT MODE: hydrate state from existing invoice ----
    const [editLoaded, setEditLoaded] = useState(false);
    const [editInvoiceNo, setEditInvoiceNo] = useState("");
    useEffect(() => {
        if (!isEdit || editLoaded) return;
        api.get(`/invoices/${editId}`)
            .then(({ data: inv }) => {
                if (!inv) return;
                setEditInvoiceNo(inv.invoice_no || "");
                // Hydrate party — try to match from already-loaded parties list
                if (inv.party_id) {
                    const p = parties.find((x) => x.id === inv.party_id);
                    if (p) setParty(p);
                    else setParty({ id: inv.party_id, name: inv.party_name, gstin: inv.party_gstin, state: inv.party_state });
                } else if (inv.party_name) {
                    setWalkInName(inv.party_name);
                }
                // Hydrate lines
                if (Array.isArray(inv.lines) && inv.lines.length) {
                    setLines(inv.lines.map((ln) => ({
                        _uid: typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `ln-${Date.now()}-${Math.random()}`,
                        item_id: ln.item_id || null,
                        name: ln.name || "",
                        hsn: ln.hsn || "",
                        qty: Number(ln.qty) || 0,
                        unit: ln.unit || "PCS",
                        rate: Number(ln.rate) || 0,
                        discount: Number(ln.discount) || 0,
                        gst_rate: Number(ln.gst_rate) || 0,
                        description: ln.description || "", colour: ln.colour || "", size: ln.size || "",
                        brand: ln.brand || "", batch_no: ln.batch_no || "", serial_no: ln.serial_no || "",
                        exp_date: ln.exp_date || "", mrp: Number(ln.mrp) || 0, free_qty: Number(ln.free_qty) || 0,
                        _expanded: false,
                    })));
                }
                setInvoiceDate(inv.invoice_date || invoiceDate);
                setNotes(inv.notes || "");
                setExtraDiscount(Number(inv.extra_discount) || 0);
                setRoundOff(Number(inv.round_off) || 0);
                setTaxInclusive(!!inv.tax_inclusive);
                setPaymentReceived(Number(inv.payment_received) || 0);
                setPaymentMode(inv.payment_mode || "Cash");
                setTransportName(inv.transport_name || "");
                setVehicleNo(inv.vehicle_no || "");
                setDeliveryLocation(inv.delivery_location || "");
                setDeliveryCharge(Number(inv.delivery_charge) || 0);
                setPackagingCharge(Number(inv.packaging_charge) || 0);
                setBillingAddress(inv.billing_address || "");
                setShippingAddress(inv.shipping_address || "");
                setBillingPhone(inv.billing_phone || "");
                setBillingName(inv.billing_name || "");
                setCopyType(inv.copy_type || "ORIGINAL");
                setTermsText(inv.terms_text || "");
                // v10.2 advanced fields — safe fallback for legacy invoices
                setDescription(inv.description || "");
                setAdjustment(Number(inv.adjustment) || 0);
                setRoundOffMode(inv.round_off_mode || "nearest_rupee");
                setAutoRoundOff(inv.auto_round_off !== undefined ? !!inv.auto_round_off : true);
                if (inv.charges && typeof inv.charges === "object") setCharges({
                    loading: Number(inv.charges.loading) || 0,
                    unloading: Number(inv.charges.unloading) || 0,
                    freight: Number(inv.charges.freight) || 0,
                    insurance: Number(inv.charges.insurance) || 0,
                    labour: Number(inv.charges.labour) || 0,
                    other: Number(inv.charges.other) || 0,
                });
                setAttachments(Array.isArray(inv.attachments) ? inv.attachments : []);
                setMixedPayments(Array.isArray(inv.mixed_payments) ? inv.mixed_payments : []);
                if (Array.isArray(inv.mixed_payments) && inv.mixed_payments.length) setShowMixedPayment(true);
                // v11: tax_mode (backward-compatible: legacy invoices default to GST)
                setTaxMode(inv.tax_mode === "NON_GST" ? "NON_GST" : "GST");
                setFinancialYear(inv.financial_year || "");
                setEditLoaded(true);
            })
            .catch((e) => {
                toast.error(e.response?.status === 404 ? "Invoice not found" : "Failed to load invoice");
                navigate(meta.listPath);
            });
    }, [isEdit, editId, editLoaded, parties, invoiceDate, meta.listPath, navigate]);

    // ---- v12 Quick Bill — Vyapar-style keyboard shortcuts on the invoice form ----
    //   Ctrl+S         → Save invoice (Save only)
    //   Ctrl+Shift+P   → Save & Print
    //   Ctrl+Shift+W   → Save & Send WhatsApp
    //   Alt+I          → Insert new empty line at the bottom
    //   Alt+C          → Focus customer/party picker
    //   Alt+N          → Focus first item input (start typing immediately)
    //   F2             → Toggle GST / Without GST tax mode
    useEffect(() => {
        const onKey = (e) => {
            // Ignore when typing inside Select / Dialog dropdowns to avoid hijacking
            if (e.target?.tagName === "TEXTAREA" && (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s") {
                // allow Ctrl+S to save even from textarea
            }
            const k = e.key.toLowerCase();
            // Ctrl+S → Save
            if ((e.ctrlKey || e.metaKey) && !e.shiftKey && k === "s") {
                e.preventDefault();
                submit({ andThen: "none" });
                return;
            }
            // Ctrl+Shift+P → Save & Print
            if ((e.ctrlKey || e.metaKey) && e.shiftKey && k === "p") {
                e.preventDefault();
                submit({ andThen: "print" });
                return;
            }
            // Ctrl+Shift+W → Save & WhatsApp
            if ((e.ctrlKey || e.metaKey) && e.shiftKey && k === "w") {
                e.preventDefault();
                submit({ andThen: "whatsapp" });
                return;
            }
            // Alt+I → Add new line
            if (e.altKey && k === "i") {
                e.preventDefault();
                setLines((ls) => [...ls, blankLine()]);
                return;
            }
            // Alt+C → Focus party picker (Customer)
            if (e.altKey && k === "c") {
                e.preventDefault();
                document.querySelector('[data-testid="party-picker"]')?.click();
                return;
            }
            // Alt+N → Focus first item search input
            if (e.altKey && k === "n") {
                e.preventDefault();
                const first = document.querySelector('[data-testid^="item-picker-"]');
                if (first) { first.focus(); first.select?.(); }
                return;
            }
            // F2 → Toggle tax mode
            if (e.key === "F2") {
                e.preventDefault();
                setTaxMode((m) => (m === "GST" ? "NON_GST" : "GST"));
                return;
            }
        };
        window.addEventListener("keydown", onKey);
        return () => window.removeEventListener("keydown", onKey);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    // ---- v11: Pre-fetch the next invoice number so user sees it BEFORE Save ----
    useEffect(() => {
        if (isEdit || !activeId) return;
        let cancelled = false;
        const fetchPreview = async () => {
            try {
                const { data } = await api.get("/txn-prefixes/preview-number", {
                    params: { company_id: activeId, type: mode },
                });
                if (!cancelled) {
                    setPreviewInvoiceNo(data?.invoice_no || "");
                    setFinancialYear(data?.financial_year || "");
                    if (data?.prefix_id) setSelectedPrefixId(data.prefix_id);
                    // Extract human-readable label from invoice_no (everything except final digits)
                    if (data?.invoice_no) {
                        const m = data.invoice_no.match(/^(.*?)(\d+)$/);
                        setPrefixSeriesLabel(m ? m[1] : data.invoice_no);
                    }
                }
            } catch (e) { /* show empty silently — backend already auto-creates default */ }
        };
        fetchPreview();
        return () => { cancelled = true; };
    }, [isEdit, activeId, mode]);

    // ---- v11 Vyapar-parity: Quick Add Party (opened from PartyPicker dropdown) ----
    const [quickAddOpen, setQuickAddOpen] = useState(false);
    const [quickAddSeed, setQuickAddSeed] = useState("");

    const openQuickAddParty = (seed = "") => {
        setQuickAddSeed(seed);
        setQuickAddOpen(true);
    };

    const handlePartyCreated = (newParty) => {
        // Push to local list so the picker shows it without a refetch
        setParties((prev) => {
            if (prev.some((p) => p.id === newParty.id)) return prev;
            return [newParty, ...prev];
        });
        // Auto-select the newly created party in the invoice
        setParty(newParty);
        setQuickAddOpen(false);
    };

    // ---- v12 Vyapar-parity: Quick Add Item (opened from ItemPicker when search yields no matches) ----
    const [quickAddItemOpen, setQuickAddItemOpen] = useState(false);
    const [quickAddItemSeed, setQuickAddItemSeed] = useState("");
    const [quickAddItemLineIdx, setQuickAddItemLineIdx] = useState(null);

    const openQuickAddItem = (lineIdx, seed = "") => {
        setQuickAddItemSeed(seed);
        setQuickAddItemLineIdx(lineIdx);
        setQuickAddItemOpen(true);
    };

    const handleItemCreated = (newItem) => {
        // Push to local list so the picker shows it without a refetch
        setItems((prev) => {
            if (prev.some((i) => i.id === newItem.id)) return prev;
            return [newItem, ...prev];
        });
        // Auto-fill the line that triggered the modal
        if (quickAddItemLineIdx !== null) {
            setLine(quickAddItemLineIdx, {
                item_id: newItem.id,
                name: newItem.name,
                hsn: newItem.hsn || "",
                unit: newItem.unit || "PCS",
                rate: newItem.sale_price || 0,
                gst_rate: newItem.gst_rate ?? 18,
            });
        }
        setQuickAddItemOpen(false);
        setQuickAddItemLineIdx(null);
    };

    // ---- Multi-window minimize ----
    const draftPayload = {
        party, walkInName, lines, extraDiscount, roundOff, taxInclusive,
        paymentReceived, paymentMode, invoiceDate, notes,
        transportName, vehicleNo, deliveryLocation, deliveryCharge, copyType, termsText,
    };
    const partyLabel = party?.name || walkInName || "Walk-in";
    const { hydratedPayload, minimize } = useMinimizable({
        kind: mode === "sale_order" ? "sale-order" : mode === "credit_note" ? "credit-note" : mode === "debit_note" ? "debit-note" : mode,
        title: `${meta.title.replace("New ", "")} · ${partyLabel}`,
        summary: { partyName: partyLabel, total: 0 /* updated below */ },
        route: `/${mode === "sale" ? "sales" : mode === "purchase" ? "purchases" : mode === "quotation" ? "quotations" : mode === "sale_order" ? "sale-orders" : mode === "proforma" ? "proforma" : mode === "credit_note" ? "credit-notes" : mode === "debit_note" ? "debit-notes" : "sales"}/new`,
        payload: draftPayload,
        returnTo: meta.listPath,
    });

    // Restore from hydrated draft once on mount
    const [hydrated, setHydrated] = useState(false);
    useEffect(() => {
        if (hydrated || !hydratedPayload) return;
        const p = hydratedPayload;
        if (p.party) setParty(p.party);
        if (p.walkInName) setWalkInName(p.walkInName);
        if (Array.isArray(p.lines) && p.lines.length) setLines(p.lines);
        if (p.extraDiscount !== undefined) setExtraDiscount(p.extraDiscount);
        if (p.roundOff !== undefined) setRoundOff(p.roundOff);
        if (p.taxInclusive !== undefined) setTaxInclusive(p.taxInclusive);
        if (p.paymentReceived !== undefined) setPaymentReceived(p.paymentReceived);
        if (p.paymentMode) setPaymentMode(p.paymentMode);
        if (p.invoiceDate) setInvoiceDate(p.invoiceDate);
        if (p.notes !== undefined) setNotes(p.notes);
        if (p.transportName !== undefined) setTransportName(p.transportName);
        if (p.vehicleNo !== undefined) setVehicleNo(p.vehicleNo);
        if (p.deliveryLocation !== undefined) setDeliveryLocation(p.deliveryLocation);
        if (p.deliveryCharge !== undefined) setDeliveryCharge(p.deliveryCharge);
        if (p.copyType) setCopyType(p.copyType);
        if (p.termsText !== undefined) setTermsText(p.termsText);
        setHydrated(true);
        toast.success("Draft restored");
    }, [hydratedPayload, hydrated]);

    // Ctrl+M minimizes the current bill
    useEffect(() => {
        const onKey = (e) => {
            if ((e.ctrlKey || e.metaKey) && !e.shiftKey && (e.key === "m" || e.key === "M")) {
                e.preventDefault();
                minimize();
            }
        };
        window.addEventListener("keydown", onKey);
        return () => window.removeEventListener("keydown", onKey);
    }, [minimize]);

    // Auto-fill billing fields when party is picked
    useEffect(() => {
        if (party) {
            setBillingName(party.name || "");
            setBillingPhone(party.phone || party.mobile || "");
            setBillingAddress(party.address || "");
            // Shipping defaults to billing unless user already overrode it
            setShippingAddress((cur) => cur || party.address || "");
        }
    }, [party]);

    const totals = useMemo(() => {
        let subtotal = 0, gstTotal = 0;
        const partyState = (party?.state || "").trim().toLowerCase();
        const companyState = (active?.state || "").trim().toLowerCase();
        const isGst = taxMode === "GST";
        const interstate = isGst && !!(partyState && companyState && partyState !== companyState);
        for (const ln of lines) {
            const qty = Number(ln.qty) || 0;
            const rate = Number(ln.rate) || 0;
            const discPct = Number(ln.discount) || 0;
            // NON_GST forces 0% tax regardless of per-line gst_rate
            const gstRate = isGst ? (Number(ln.gst_rate) || 0) : 0;
            let taxable, gst;
            if (taxInclusive && gstRate > 0) {
                const gross = qty * rate;
                const ga = gross - gross * (discPct / 100);
                taxable = ga / (1 + gstRate / 100);
                gst = ga - taxable;
            } else {
                const lt = qty * rate;
                const ld = lt * (discPct / 100);
                taxable = lt - ld;
                gst = taxable * (gstRate / 100);
            }
            subtotal += taxable;
            gstTotal += gst;
        }
        const chargesTotal = ["loading", "unloading", "freight", "insurance", "labour", "other"]
            .reduce((acc, k) => acc + (Number(charges[k]) || 0), 0);
        const preRound = subtotal + gstTotal
            - (Number(extraDiscount) || 0)
            + (Number(deliveryCharge) || 0)
            + (Number(packagingCharge) || 0)
            + chargesTotal
            + (Number(adjustment) || 0);
        // Compute auto round-off based on selected mode
        let ro = Number(roundOff) || 0;
        if (autoRoundOff) {
            if (roundOffMode === "nearest_rupee") {
                ro = Math.round(preRound) - preRound;
            } else if (roundOffMode === "nearest_50p") {
                ro = (Math.round(preRound * 2) / 2) - preRound;
            }
        }
        const total = preRound + ro;
        return {
            subtotal: Math.max(0, subtotal),
            gst: Math.max(0, gstTotal),
            cgst: interstate ? 0 : Math.max(0, gstTotal / 2),
            sgst: interstate ? 0 : Math.max(0, gstTotal / 2),
            igst: interstate ? Math.max(0, gstTotal) : 0,
            interstate,
            chargesTotal,
            roundOffEffective: ro,
            total: Math.max(0, total),
        };
    }, [lines, extraDiscount, roundOff, taxInclusive, deliveryCharge, packagingCharge, party?.state, active?.state, charges, adjustment, autoRoundOff, roundOffMode, taxMode]);

    const setLine = (idx, patch) => setLines((ls) => ls.map((l, i) => (i === idx ? { ...l, ...patch } : l)));
    const removeLine = (idx) => setLines((ls) => ls.length > 1 ? ls.filter((_, i) => i !== idx) : ls);
    const pickItem = (idx, it) => {
        setLine(idx, {
            item_id: it.id,
            name: it.name,
            hsn: it.hsn || "",
            unit: it.unit || "PCS",
            rate: mode === "purchase" ? Number(it.purchase_price || 0) : Number(it.sale_price || 0),
            gst_rate: Number(it.gst_rate || 18),
        });
    };

    const submit = async ({ andThen = "none" } = {}) => {
        if (!activeId) { toast.error("Select a company first"); return; }
        const cleanLines = lines.filter((l) => l.name && Number(l.qty) > 0);
        if (cleanLines.length === 0) { toast.error("Add at least one item"); return; }
        setBusy(true);
        try {
            const payload = {
                type: mode,
                tax_mode: taxMode,                                       // v11: GST or NON_GST
                prefix_id: selectedPrefixId || null,                     // v12: user-selected series (null = default)
                invoice_no_override: invoiceNoMode === "custom" && customInvoiceNo.trim() ? customInvoiceNo.trim() : null,
                party_id: party?.id || null,
                party_name: party?.name || walkInName || "Walk-in Customer",
                party_gstin: party?.gstin || "",
                party_state: party?.state || "",
                invoice_date: invoiceDate,
                lines: cleanLines.map((l) => ({
                    item_id: l.item_id || null,
                    name: l.name,
                    hsn: l.hsn || "",
                    qty: Number(l.qty) || 0,
                    unit: l.unit || "PCS",
                    rate: Number(l.rate) || 0,
                    discount: Number(l.discount) || 0,
                    gst_rate: Number(l.gst_rate) || 0,
                    // Extended fields
                    description: l.description || "",
                    colour: l.colour || "",
                    size: l.size || "",
                    brand: l.brand || "",
                    batch_no: l.batch_no || "",
                    serial_no: l.serial_no || "",
                    exp_date: l.exp_date || "",
                    mrp: Number(l.mrp) || 0,
                    free_qty: Number(l.free_qty) || 0,
                })),
                extra_discount: Number(extraDiscount) || 0,
                round_off: Number(roundOff) || 0,
                tax_inclusive: !!taxInclusive,
                payment_received: Number(paymentReceived) || 0,
                payment_mode: paymentMode,
                payment_terms: paymentTerms,
                billing_name: billingName,
                billing_phone: billingPhone,
                billing_address: billingAddress,
                shipping_address: shippingAddress,
                packaging_charge: Number(packagingCharge) || 0,
                notes,
                transport_name: transportName,
                vehicle_no: vehicleNo,
                delivery_location: deliveryLocation,
                delivery_charge: Number(deliveryCharge) || 0,
                copy_type: copyType,
                terms_text: termsText,
                // v10.2 Vyapar-parity advanced fields
                description: description || "",
                adjustment: Number(adjustment) || 0,
                round_off_mode: roundOffMode,
                auto_round_off: !!autoRoundOff,
                charges: {
                    loading: Number(charges.loading) || 0,
                    unloading: Number(charges.unloading) || 0,
                    freight: Number(charges.freight) || 0,
                    insurance: Number(charges.insurance) || 0,
                    labour: Number(charges.labour) || 0,
                    other: Number(charges.other) || 0,
                },
                attachments: attachments,
                mixed_payments: showMixedPayment ? mixedPayments : [],
            };
            // If offline → queue and toast (only for create; edit is server-only)
            if (!navigator.onLine && !isEdit) {
                const { enqueueMutation } = await import("@/lib/localdb");
                await enqueueMutation("invoice", {
                    method: "POST",
                    url: `/invoices?company_id=${activeId}`,
                    body: payload,
                    summary: `${meta.title.replace("New ", "")} · ${payload.party_name} · ₹ ${(payload.lines.reduce((s, l) => s + l.qty * l.rate, 0)).toFixed(2)}`,
                });
                toast.success("Saved offline — will sync when online.");
                navigate(meta.listPath);
                return;
            }
            if (isEdit) {
                const { data } = await api.put(`/invoices/${editId}`, payload);
                toast.success(`Invoice ${data.invoice_no} updated`);
                // v12.37 — offer "Send WhatsApp now?" CTA (no-op if shortcut disabled)
                offerWaShortcut({
                    companyId: activeId,
                    eventKey: INVOICE_TYPE_TO_EVENT[payload.type],
                    txnId: data.id,
                    party: data.party_name,
                });
                navigate(`${meta.listPath}/${data.id}`);
            } else {
                const { data } = await api.post(`/invoices?company_id=${activeId}`, payload);
                toast.success(`${meta.title.replace("New ", "")} created: ${data.invoice_no}`);
                // v12.37 — offer "Send WhatsApp now?" CTA (no-op if shortcut disabled)
                offerWaShortcut({
                    companyId: activeId,
                    eventKey: INVOICE_TYPE_TO_EVENT[payload.type],
                    txnId: data.id,
                    party: data.party_name,
                });
                // Handle Smart Save post-action (Print / Share / WhatsApp / Email)
                if (andThen === "print") {
                    navigate(`${meta.listPath}/${data.id}?action=print`);
                } else if (andThen === "share") {
                    navigate(`${meta.listPath}/${data.id}?action=share`);
                } else if (andThen === "whatsapp") {
                    navigate(`${meta.listPath}/${data.id}?action=whatsapp`);
                } else if (andThen === "email") {
                    navigate(`${meta.listPath}/${data.id}?action=email`);
                } else {
                    navigate(`${meta.listPath}/${data.id}`);
                }
            }
        } catch (e) {
            toast.error(formatApiError(e.response?.data?.detail));
        } finally {
            setBusy(false);
        }
    };

    // ---- OCR auto-fill ----
    const applyOcr = async (extracted) => {
        if (!extracted) return;
        // Duplicate invoice detection (best-effort)
        if (extracted.invoice_no && activeId) {
            try {
                const { data: existing } = await api.get(`/invoices`, {
                    params: { company_id: activeId, type: mode, q: extracted.invoice_no },
                });
                const dup = (existing || []).find((d) => (d.invoice_no || "").toLowerCase() === extracted.invoice_no.toLowerCase());
                if (dup) {
                    toast.warning(`Possible duplicate — invoice ${extracted.invoice_no} already exists.`, { duration: 6000 });
                }
            } catch (err) {
                console.warn("Duplicate invoice check failed", err);
            }
        }
        // Party autofill — match by name within parties list
        if (extracted.vendor_name) {
            const v = extracted.vendor_name.toLowerCase();
            const match = parties.find((p) => (p.name || "").toLowerCase().includes(v));
            if (match) setParty(match);
            else setWalkInName(extracted.vendor_name);
        }
        if (extracted.date) setInvoiceDate(extracted.date);
        if (extracted.payment_mode) setPaymentMode(extracted.payment_mode);
        if (Array.isArray(extracted.lines) && extracted.lines.length) {
            const mapped = extracted.lines.map((ln) => {
                const itemMatch = items.find((it) =>
                    (it.name || "").toLowerCase().includes((ln.name || "").toLowerCase()) ||
                    (ln.name || "").toLowerCase().includes((it.name || "").toLowerCase()),
                );
                const qty = Number(ln.qty) > 0 ? Number(ln.qty) : 1;
                const rate = Number(ln.rate) > 0 ? Number(ln.rate) : (Number(ln.amount) || 0) / qty;
                return {
                    _uid: typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `ln-${Date.now()}-${Math.random()}`,
                    item_id: itemMatch?.id || null,
                    name: itemMatch?.name || ln.name || "",
                    hsn: ln.hsn || itemMatch?.hsn || "",
                    qty,
                    unit: itemMatch?.base_unit || itemMatch?.unit || "PCS",
                    rate: Math.round(rate * 100) / 100,
                    discount: 0,
                    gst_rate: itemMatch?.gst_rate ?? 18,
                };
            });
            if (mapped.length) setLines(mapped);
        }
        if (extracted.notes) setNotes(extracted.notes);
    };

    // ============ Voice-to-Invoice handler ============
    const [voiceOpen, setVoiceOpen] = useState(false);
    const applyVoice = (draft) => {
        if (!draft) return;
        // Party
        if (draft.party) {
            setParty(draft.party);
        } else if (draft.party_name_hint) {
            setWalkInName(draft.party_name_hint);
        }
        // Payment
        if (draft.payment_mode) setPaymentMode(draft.payment_mode);
        if (typeof draft.payment_received === "number" && draft.payment_received > 0) {
            setPaymentReceived(draft.payment_received);
        }
        // Lines
        if (Array.isArray(draft.lines) && draft.lines.length) {
            const mapped = draft.lines.map((ln) => {
                const itemMatch = ln.matched_item_id
                    ? items.find((it) => it.id === ln.matched_item_id)
                    : items.find((it) => (it.name || "").toLowerCase() === (ln.name || "").toLowerCase());
                return {
                    _uid: typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `ln-${Date.now()}-${Math.random()}`,
                    item_id: itemMatch?.id || null,
                    name: itemMatch?.name || ln.name || "",
                    hsn: ln.matched_item_hsn || itemMatch?.hsn || "",
                    qty: Number(ln.qty) > 0 ? Number(ln.qty) : 1,
                    unit: (ln.unit || itemMatch?.unit || "PCS").toUpperCase(),
                    rate: Number(ln.rate) > 0 ? Number(ln.rate) : (itemMatch?.sale_price || 0),
                    discount: Number(ln.discount) || 0,
                    gst_rate: typeof ln.gst_rate === "number" ? ln.gst_rate : (itemMatch?.gst_rate ?? 18),
                    description: "", colour: "", size: "", brand: "", batch_no: "", serial_no: "",
                    mfg_date: "", exp_date: "",
                };
            });
            setLines(mapped);
        }
        if (draft.notes) setNotes((cur) => (cur ? `${cur}\n${draft.notes}` : draft.notes));
    };

    return (
        <div className="space-y-6" data-testid="new-invoice-page">
            <div className="flex items-center justify-between flex-wrap gap-3">
                <div className="flex items-center gap-3">
                    <Button variant="ghost" size="sm" onClick={() => navigate(-1)} data-testid="back-button"><ArrowLeft className="h-4 w-4 mr-1.5" /> Back</Button>
                    <div>
                        <div className="label-cap flex items-center gap-2">
                            Transaction
                            {financialYear && <span className="font-mono text-[10px] text-muted-foreground">FY {financialYear}</span>}
                        </div>
                        <h1 className="font-display text-2xl md:text-3xl font-bold tracking-tight flex items-center gap-2 flex-wrap">
                            <span>{isEdit ? meta.title.replace("New ", "Edit ") : meta.title}</span>
                            {isEdit && editInvoiceNo && <span className="font-mono text-base text-muted-foreground">{editInvoiceNo}</span>}
                            {!isEdit && previewInvoiceNo && (
                                <span className="font-mono text-base text-primary inline-flex items-center gap-1.5" data-testid="invoice-number-preview" title="Next invoice number — will be assigned on Save">
                                    {/* Vyapar-style — clickable prefix dropdown next to the number */}
                                    {activeId && invoiceNoMode === "auto" && (
                                        <InvoicePrefixPicker
                                            companyId={activeId}
                                            type={mode}
                                            value={selectedPrefixId}
                                            currentLabel={prefixSeriesLabel || previewInvoiceNo}
                                            onChange={setSelectedPrefixId}
                                            onNumberChange={(num, fy) => { setPreviewInvoiceNo(num); if (fy) setFinancialYear(fy); const m = num.match(/^(.*?)(\d+)$/); setPrefixSeriesLabel(m ? m[1] : num); }}
                                        />
                                    )}
                                    {invoiceNoMode === "auto" ? (
                                        <span className="text-base">#{previewInvoiceNo.match(/\d+$/)?.[0] || previewInvoiceNo}</span>
                                    ) : (
                                        <Input
                                            value={customInvoiceNo}
                                            onChange={(e) => setCustomInvoiceNo(e.target.value)}
                                            placeholder={previewInvoiceNo}
                                            className="h-7 w-44 font-mono text-base text-primary"
                                            data-testid="custom-invoice-no-input"
                                            autoFocus
                                        />
                                    )}
                                    {/* Auto / Custom toggle pill — Vyapar parity */}
                                    <span className="flex items-center gap-0 rounded-md border border-border bg-muted/30 p-0.5 ml-1" data-testid="invoice-no-mode-toggle">
                                        <button
                                            type="button"
                                            onClick={() => { setInvoiceNoMode("auto"); setCustomInvoiceNo(""); }}
                                            className={`px-2 py-0.5 text-[10px] font-semibold rounded transition-colors ${invoiceNoMode === "auto" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`}
                                            data-testid="invoice-no-mode-auto"
                                            title="Auto — next number from selected series"
                                        >Auto</button>
                                        <button
                                            type="button"
                                            onClick={() => { setInvoiceNoMode("custom"); setCustomInvoiceNo(previewInvoiceNo); }}
                                            className={`px-2 py-0.5 text-[10px] font-semibold rounded transition-colors ${invoiceNoMode === "custom" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`}
                                            data-testid="invoice-no-mode-custom"
                                            title="Custom — type your own invoice number"
                                        >Custom</button>
                                    </span>
                                </span>
                            )}
                        </h1>
                    </div>
                </div>
                <div className="flex items-center gap-2 flex-wrap">
                    {/* Vyapar-parity: search any past invoice/bill inline from this screen */}
                    {!isEdit && activeId && <OldBillSearch companyId={activeId} type={mode} />}
                    {/* v11: GST / Without GST tax mode toggle — hides tax fields in NON_GST */}
                    {(mode === "sale" || mode === "purchase" || mode === "challan" || mode === "credit_note" || mode === "debit_note") && (
                        <div className="flex items-center gap-0.5 rounded-lg border border-border bg-muted/30 p-0.5" data-testid="tax-mode-toggle" title="Switch between GST and Non-GST billing">
                            <button
                                type="button"
                                onClick={() => setTaxMode("GST")}
                                className={`px-3 py-1.5 text-xs font-semibold rounded-md transition-colors ${taxMode === "GST" ? "bg-emerald-500 text-white shadow-sm" : "text-muted-foreground hover:text-foreground"}`}
                                data-testid="tax-mode-gst"
                            >GST Bill</button>
                            <button
                                type="button"
                                onClick={() => setTaxMode("NON_GST")}
                                className={`px-3 py-1.5 text-xs font-semibold rounded-md transition-colors ${taxMode === "NON_GST" ? "bg-slate-700 dark:bg-slate-200 dark:text-slate-900 text-white shadow-sm" : "text-muted-foreground hover:text-foreground"}`}
                                data-testid="tax-mode-non-gst"
                            >Without GST</button>
                        </div>
                    )}
                    <Badge variant="outline" className="text-[10px] font-mono uppercase border-amber-400/40 text-amber-600 dark:text-amber-400">{copyType}</Badge>
                    <Badge variant="secondary" className="capitalize text-xs">{mode}</Badge>
                    <Button
                        size="sm"
                        variant="outline"
                        onClick={minimize}
                        title="Minimize (Ctrl+M)"
                        data-testid="invoice-minimize-button"
                    >
                        <Minimize2 className="h-4 w-4 mr-1.5" /> Minimize
                    </Button>
                </div>
            </div>

            {/* Header card */}
            <Card><CardContent className="p-5 grid grid-cols-1 md:grid-cols-4 gap-4">
                <div className="md:col-span-2 space-y-1.5">
                    <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">{meta.label}</Label>
                    <PartyPicker
                        parties={parties}
                        value={party}
                        onSelect={setParty}
                        onAddNew={openQuickAddParty}
                        testid="party-picker"
                    />
                    {!party && (
                        <Input placeholder="Or enter walk-in name" value={walkInName} onChange={(e) => setWalkInName(e.target.value)} className="mt-2" data-testid="walkin-name-input" />
                    )}
                </div>
                <div className="space-y-1.5">
                    <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Invoice Date</Label>
                    <Input type="date" value={invoiceDate} onChange={(e) => setInvoiceDate(e.target.value)} data-testid="invoice-date" />
                </div>
                <div className="space-y-1.5">
                    <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Payment Mode</Label>
                    <Select value={paymentMode} onValueChange={setPaymentMode}>
                        <SelectTrigger data-testid="payment-mode-select"><SelectValue /></SelectTrigger>
                        <SelectContent>
                            {["Cash", "UPI", "Bank Transfer", "Card", "Cheque", "Credit"].map((p) => <SelectItem key={p} value={p}>{p}</SelectItem>)}
                        </SelectContent>
                    </Select>
                </div>
            </CardContent></Card>

            {/* Billing & Shipping addresses + payment terms */}
            <Card><CardContent className="p-5 space-y-4">
                <div className="flex items-center justify-between flex-wrap gap-3">
                    <div className="flex items-center gap-2">
                        <MapPin className="h-4 w-4 text-primary" />
                        <h3 className="font-display text-sm font-semibold uppercase tracking-wider text-primary">Billing & Shipping</h3>
                    </div>
                    <div className="flex items-center gap-1 rounded-lg border border-border bg-muted/30 p-1" data-testid="payment-terms-toggle">
                        <button
                            type="button"
                            onClick={() => { setPaymentTerms("credit"); setPaymentMode("Credit"); }}
                            className={`px-4 py-1.5 text-xs font-semibold rounded-md transition-colors ${paymentTerms === "credit" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}
                            data-testid="payment-credit-btn"
                        >Credit</button>
                        <button
                            type="button"
                            onClick={() => { setPaymentTerms("cash"); setPaymentMode("Cash"); }}
                            className={`px-4 py-1.5 text-xs font-semibold rounded-md transition-colors ${paymentTerms === "cash" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}
                            data-testid="payment-cash-btn"
                        >Cash</button>
                    </div>
                </div>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div className="space-y-1.5">
                        <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Billing Name</Label>
                        <Input value={billingName} onChange={(e) => setBillingName(e.target.value)} placeholder="Customer / Firm name" data-testid="billing-name-input" />
                    </div>
                    <div className="space-y-1.5">
                        <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Phone No.</Label>
                        <Input value={billingPhone} onChange={(e) => setBillingPhone(e.target.value)} placeholder="+91 ..." data-testid="billing-phone-input" />
                    </div>
                    <div className="space-y-1.5">
                        <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Billing Address</Label>
                        <Textarea value={billingAddress} onChange={(e) => setBillingAddress(e.target.value)} rows={2} placeholder="Street, City, State, PIN" data-testid="billing-address-input" />
                    </div>
                    <div className="space-y-1.5">
                        <div className="flex items-center justify-between">
                            <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Shipping Address</Label>
                            <button type="button" onClick={() => setShippingAddress(billingAddress)} className="text-[10px] text-primary hover:underline" data-testid="same-as-billing-btn">Same as billing</button>
                        </div>
                        <Textarea value={shippingAddress} onChange={(e) => setShippingAddress(e.target.value)} rows={2} placeholder="Delivery address (if different)" data-testid="shipping-address-input" />
                    </div>
                </div>
            </CardContent></Card>

            {/* Bill Scan + Voice-to-Invoice moved to right summary panel
                (next to Grand Total → Payment → Balance → Save).
                Modal stays mounted here so its state survives column scroll. */}
            <VoiceToInvoice
                open={voiceOpen}
                onOpenChange={setVoiceOpen}
                companyId={activeId}
                type={mode}
                onApply={applyVoice}
            />

            {/* Items table */}
            <Card><CardContent className="p-0">
                <div className="px-5 py-3 border-b border-border flex items-center justify-between flex-wrap gap-2">
                    <div className="flex items-center gap-3">
                        <div className="label-cap">Line Items</div>
                        <Badge variant="outline" className="text-[10px]">{lines.length} {lines.length === 1 ? "row" : "rows"}</Badge>
                    </div>
                    <div className="flex items-center gap-2">
                        <label className="text-xs flex items-center gap-1.5 cursor-pointer">
                            <input type="checkbox" checked={showExtraCols} onChange={(e) => setShowExtraCols(e.target.checked)} data-testid="toggle-extra-cols" />
                            <span>Show extra columns</span>
                        </label>
                        <Button size="sm" variant="outline" onClick={() => setLines([...lines, blankLine()])} data-testid="add-line-button">
                            <Plus className="h-3.5 w-3.5 mr-1" /> Add Row
                        </Button>
                    </div>
                </div>
                <div className="overflow-x-auto">
                    <table className="w-full text-sm dense-table">
                        <thead className="bg-muted/40">
                            <tr className="text-left text-[10px] tracking-wider uppercase text-muted-foreground">
                                <th className="px-3 py-2.5 w-10">#</th>
                                <th className="w-[20%] min-w-[180px]">Item</th>
                                <th>HSN</th>
                                {showExtraCols && <th>Colour</th>}
                                {showExtraCols && <th>Size</th>}
                                {showExtraCols && <th>Brand</th>}
                                {showExtraCols && <th>Batch No</th>}
                                {showExtraCols && <th>Serial No</th>}
                                {showExtraCols && <th>Exp Date</th>}
                                {showExtraCols && <th className="text-right">MRP</th>}
                                <th className="text-right">Qty</th>
                                {showExtraCols && <th className="text-right">Free</th>}
                                <th>Unit</th>
                                <th className="text-right">
                                    {/* Vyapar-style — clickable header to switch Rate between With Tax / Without Tax */}
                                    {taxMode === "GST" ? (
                                        <Select value={taxInclusive ? "incl" : "excl"} onValueChange={(v) => setTaxInclusive(v === "incl")}>
                                            <SelectTrigger className="h-7 ml-auto text-[10px] tracking-wider uppercase border-0 bg-transparent hover:bg-muted/50 px-2 -mr-1 w-[140px]" data-testid="rate-tax-mode-header">
                                                <SelectValue>
                                                    Rate <span className="text-primary font-semibold normal-case tracking-normal">({taxInclusive ? "With Tax" : "Without Tax"})</span>
                                                </SelectValue>
                                            </SelectTrigger>
                                            <SelectContent>
                                                <SelectItem value="excl">Without Tax</SelectItem>
                                                <SelectItem value="incl">With Tax</SelectItem>
                                            </SelectContent>
                                        </Select>
                                    ) : "Rate (₹)"}
                                </th>
                                <th className="text-right">Disc %</th>
                                {taxMode === "GST" && <th className="text-left pl-1">Tax</th>}
                                <th className="text-right">Amount</th>
                                <th className="pr-3 w-10"></th>
                            </tr>
                        </thead>
                        <tbody>
                            {lines.map((ln, idx) => {
                                const lt = (Number(ln.qty) || 0) * (Number(ln.rate) || 0);
                                const ld = lt * ((Number(ln.discount) || 0) / 100);
                                const taxable = lt - ld;
                                const effectiveGst = taxMode === "GST" ? (Number(ln.gst_rate) || 0) : 0;
                                const total = taxable + taxable * (effectiveGst / 100);
                                const colSpan = showExtraCols ? 18 : 10;
                                return (
                                    <React.Fragment key={ln._uid || idx}>
                                        <tr className="border-t border-border animate-in fade-in slide-in-from-top-1 duration-200">
                                            <td className="px-3 py-1.5 text-center text-xs font-mono text-muted-foreground" data-testid={`line-sr-${idx}`}>{idx + 1}</td>
                                            <td>
                                                <ItemPicker items={items} value={ln} onChange={(it) => pickItem(idx, it)} onTypeName={(name) => setLine(idx, { name, item_id: null })} onAddNew={(seed) => openQuickAddItem(idx, seed)} testid={`item-picker-${idx}`} />
                                            </td>
                                            <td><Input value={ln.hsn} onChange={(e) => setLine(idx, { hsn: e.target.value })} className="h-8 w-20 font-mono text-xs" /></td>
                                            {showExtraCols && <td><Input value={ln.colour} onChange={(e) => setLine(idx, { colour: e.target.value })} className="h-8 w-20 text-xs" data-testid={`line-colour-${idx}`} /></td>}
                                            {showExtraCols && <td><Input value={ln.size} onChange={(e) => setLine(idx, { size: e.target.value })} className="h-8 w-16 text-xs" /></td>}
                                            {showExtraCols && <td><Input value={ln.brand} onChange={(e) => setLine(idx, { brand: e.target.value })} className="h-8 w-24 text-xs" /></td>}
                                            {showExtraCols && <td><Input value={ln.batch_no} onChange={(e) => setLine(idx, { batch_no: e.target.value })} className="h-8 w-24 text-xs font-mono" data-testid={`line-batch-${idx}`} /></td>}
                                            {showExtraCols && <td><Input value={ln.serial_no} onChange={(e) => setLine(idx, { serial_no: e.target.value })} className="h-8 w-24 text-xs font-mono" /></td>}
                                            {showExtraCols && <td><Input type="date" value={ln.exp_date} onChange={(e) => setLine(idx, { exp_date: e.target.value })} className="h-8 w-32 text-xs" /></td>}
                                            {showExtraCols && <td className="text-right"><Input type="number" value={ln.mrp} onChange={(e) => setLine(idx, { mrp: e.target.value })} className="h-8 w-20 text-right num" /></td>}
                                            <td className="text-right"><Input type="number" value={ln.qty} onChange={(e) => setLine(idx, { qty: e.target.value })} className="h-8 w-20 text-right num" data-testid={`line-qty-${idx}`} /></td>
                                            {showExtraCols && <td className="text-right"><Input type="number" value={ln.free_qty} onChange={(e) => setLine(idx, { free_qty: e.target.value })} className="h-8 w-16 text-right num" /></td>}
                                            <td><Input value={ln.unit} onChange={(e) => setLine(idx, { unit: e.target.value })} className="h-8 w-16 text-xs" /></td>
                                            <td className="text-right"><Input type="number" value={ln.rate} onChange={(e) => setLine(idx, { rate: e.target.value })} className="h-8 w-24 text-right num" data-testid={`line-rate-${idx}`} /></td>
                                            <td className="text-right"><Input type="number" value={ln.discount} onChange={(e) => setLine(idx, { discount: e.target.value })} className="h-8 w-16 text-right num" /></td>
                                            {taxMode === "GST" && (
                                                <td>
                                                    <ItemTaxSelect
                                                        value={ln.gst_rate}
                                                        onChange={(v) => setLine(idx, { gst_rate: v })}
                                                        interstate={totals.interstate}
                                                        testid={`line-tax-${idx}`}
                                                    />
                                                </td>
                                            )}
                                            <td className="text-right num font-medium pr-1">{formatINR(total)}</td>
                                            <td className="pr-3">
                                                <div className="flex items-center gap-0.5">
                                                    <Button size="icon" variant="ghost" className="h-7 w-7" onClick={() => setLine(idx, { _expanded: !ln._expanded })} title={ln._expanded ? "Hide description" : "Add description"} data-testid={`expand-line-${idx}`}>
                                                        <FileText className="h-3.5 w-3.5 text-muted-foreground" />
                                                    </Button>
                                                    <Button size="icon" variant="ghost" className="h-7 w-7 text-destructive" onClick={() => removeLine(idx)} data-testid={`remove-line-${idx}`}>
                                                        <Trash2 className="h-3.5 w-3.5" />
                                                    </Button>
                                                </div>
                                            </td>
                                        </tr>
                                        {ln._expanded && (
                                            <tr className="border-t border-dashed border-border bg-muted/10 animate-in fade-in slide-in-from-top-1 duration-150">
                                                <td colSpan={colSpan} className="px-5 py-2">
                                                    <div className="flex items-start gap-2">
                                                        <Label className="text-[10px] uppercase tracking-wider text-muted-foreground pt-2 flex-shrink-0">Description</Label>
                                                        <Textarea
                                                            value={ln.description}
                                                            onChange={(e) => setLine(idx, { description: e.target.value })}
                                                            rows={2}
                                                            placeholder="Detailed description for this row…"
                                                            className="text-xs flex-1"
                                                            data-testid={`line-desc-${idx}`}
                                                        />
                                                    </div>
                                                </td>
                                            </tr>
                                        )}
                                    </React.Fragment>
                                );
                            })}
                        </tbody>
                    </table>
                </div>
                <div className="px-5 py-3 border-t border-border flex items-center gap-2 flex-wrap bg-muted/10">
                    <Button size="sm" variant="outline" onClick={() => setLines([...lines, blankLine()])} data-testid="add-row-bottom-btn">
                        <Plus className="h-3.5 w-3.5 mr-1.5" /> Add Row
                    </Button>
                    <Button size="sm" variant="ghost" onClick={() => {
                        // Expand last row's description toggle
                        if (lines.length > 0) setLine(lines.length - 1, { _expanded: true });
                    }} data-testid="add-description-btn">
                        <FileText className="h-3.5 w-3.5 mr-1.5" /> Add Description
                    </Button>
                    <Button size="sm" variant="ghost" disabled title="Coming soon" data-testid="add-image-btn">
                        <ImageIcon className="h-3.5 w-3.5 mr-1.5" /> Add Image
                    </Button>
                    <Button size="sm" variant="ghost" disabled title="Coming soon" data-testid="add-document-btn">
                        <Paperclip className="h-3.5 w-3.5 mr-1.5" /> Add Document
                    </Button>
                </div>
            </CardContent></Card>

            {/* Totals & payment */}
            <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
                <Card className="lg:col-span-2"><CardContent className="p-5 space-y-4">
                    {/* Description (printed on invoice) */}
                    <div>
                        <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Description (prints on invoice)</Label>
                        <Textarea
                            value={description}
                            onChange={(e) => setDescription(e.target.value)}
                            placeholder="Product notes · Service notes · Delivery notes…"
                            rows={3}
                            className="mt-1.5"
                            data-testid="invoice-description"
                        />
                    </div>

                    {/* v12 — Logistics & Document Copy (moved below Description per Vyapar layout) */}
                    <div className="rounded-lg border border-dashed border-border bg-muted/20 p-4">
                        <div className="flex items-center gap-2 mb-3">
                            <Truck className="h-4 w-4 text-primary" />
                            <h3 className="font-display text-sm font-semibold uppercase tracking-wider text-primary">Logistics & Document Copy</h3>
                        </div>
                        <div className="grid grid-cols-1 md:grid-cols-4 gap-3">
                            <div className="space-y-1.5">
                                <Label className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">Transport Name</Label>
                                <Input value={transportName} onChange={(e) => setTransportName(e.target.value)} placeholder="e.g., Gati Logistics" className="h-9" data-testid="transport-name" />
                            </div>
                            <div className="space-y-1.5">
                                <Label className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">Vehicle No.</Label>
                                <Input value={vehicleNo} onChange={(e) => setVehicleNo(e.target.value)} placeholder="GA-01-AB-1234" className="h-9 font-mono uppercase" />
                            </div>
                            <div className="space-y-1.5 md:col-span-2">
                                <Label className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">Delivery Location</Label>
                                <Input value={deliveryLocation} onChange={(e) => setDeliveryLocation(e.target.value)} placeholder="Full delivery address" className="h-9" data-testid="delivery-location" />
                            </div>
                            <div className="space-y-1.5">
                                <Label className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">Delivery Charge (₹)</Label>
                                <Input type="number" value={deliveryCharge} onChange={(e) => setDeliveryCharge(e.target.value)} className="h-9 num text-right" data-testid="delivery-charge" />
                            </div>
                            <div className="space-y-1.5 md:col-span-3">
                                <Label className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">Print Copy Type</Label>
                                <Select value={copyType} onValueChange={setCopyType}>
                                    <SelectTrigger className="h-9" data-testid="copy-type-select"><SelectValue /></SelectTrigger>
                                    <SelectContent>
                                        <SelectItem value="ORIGINAL">Original (for Buyer)</SelectItem>
                                        <SelectItem value="DUPLICATE">Duplicate (for Transporter)</SelectItem>
                                        <SelectItem value="TRIPLICATE">Triplicate (for Seller)</SelectItem>
                                    </SelectContent>
                                </Select>
                            </div>
                        </div>
                    </div>

                    {/* Terms & Conditions with template picker */}
                    <div>
                        <div className="flex items-center justify-between mb-1.5">
                            <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Terms & Conditions</Label>
                            {termsTemplates.length > 0 && (
                                <Select onValueChange={(id) => {
                                    const tpl = termsTemplates.find((t) => t.id === id);
                                    if (tpl) {
                                        setTermsText(tpl.body || "");
                                        // Fire-and-forget usage counter
                                        try { api.post(`/terms-templates/${id}/touch-usage`); } catch { /* ignore */ }
                                    }
                                }}>
                                    <SelectTrigger className="h-8 w-56 text-xs" data-testid="terms-template-select">
                                        <SelectValue placeholder="Pick template…" />
                                    </SelectTrigger>
                                    <SelectContent>
                                        {termsTemplates.map((t) => (
                                            <SelectItem key={t.id} value={t.id}>
                                                {t.is_default ? "★ " : ""}{t.name} ({t.category})
                                            </SelectItem>
                                        ))}
                                    </SelectContent>
                                </Select>
                            )}
                        </div>
                        <Textarea
                            value={termsText}
                            onChange={(e) => setTermsText(e.target.value)}
                            placeholder="Terms & Conditions auto-prints on PDF"
                            rows={4}
                            data-testid="terms-text"
                        />
                    </div>

                    {/* Internal Notes (not printed) */}
                    <div>
                        <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Internal Notes (not printed)</Label>
                        <Textarea value={notes} onChange={(e) => setNotes(e.target.value)} placeholder="Internal remarks for your team…" rows={2} className="mt-1.5" data-testid="invoice-notes" />
                    </div>

                    {/* Attachments */}
                    <div>
                        <Label className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Attachments (images / PDF)</Label>
                        <input
                            type="file"
                            accept="image/*,application/pdf"
                            multiple
                            onChange={async (e) => {
                                const files = Array.from(e.target.files || []);
                                const newOnes = await Promise.all(files.map(async (f) => {
                                    const data_url = await new Promise((res) => {
                                        const r = new FileReader();
                                        r.onload = () => res(r.result);
                                        r.readAsDataURL(f);
                                    });
                                    return { name: f.name, size: f.size, mime: f.type, data_url };
                                }));
                                setAttachments((prev) => [...prev, ...newOnes]);
                                e.target.value = "";
                            }}
                            className="mt-1.5 text-xs"
                            data-testid="invoice-attachments-input"
                        />
                        {attachments.length > 0 && (
                            <div className="mt-2 flex flex-wrap gap-1.5">
                                {attachments.map((a, i) => (
                                    <span key={i} className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-muted text-xs" data-testid={`attachment-chip-${i}`}>
                                        <span className="truncate max-w-[160px]">{a.name}</span>
                                        <button type="button" onClick={() => setAttachments((p) => p.filter((_, idx) => idx !== i))} className="text-muted-foreground hover:text-rose-600 ml-1">×</button>
                                    </span>
                                ))}
                            </div>
                        )}
                    </div>
                </CardContent></Card>
                <Card><CardContent className="p-5 space-y-2 text-sm">
                    <Row label={t("inv.subtotal")} value={formatINR(totals.subtotal)} />
                    {taxMode === "GST" && (
                        <>
                            {totals.interstate ? (
                                <Row label="IGST" value={formatINR(totals.igst)} muted />
                            ) : (
                                <>
                                    <Row label="CGST" value={formatINR(totals.cgst)} muted />
                                    <Row label="SGST" value={formatINR(totals.sgst)} muted />
                                </>
                            )}
                            {totals.interstate && (
                                <div className="text-[10px] uppercase tracking-wider text-amber-600 dark:text-amber-400 font-semibold">
                                    Inter-state · IGST applied
                                </div>
                            )}
                        </>
                    )}
                    {taxMode === "NON_GST" && (
                        <div className="text-[10px] uppercase tracking-wider text-slate-600 dark:text-slate-400 font-semibold py-1 px-2 rounded-md bg-slate-100 dark:bg-slate-900/40">
                            Without GST Bill · No tax applied
                        </div>
                    )}
                    <div className="flex items-center justify-between gap-3 pt-1">
                        <Label className="text-xs text-muted-foreground">{t("inv.taxInclusive")}</Label>
                        <Switch checked={taxInclusive} onCheckedChange={setTaxInclusive} disabled={taxMode === "NON_GST"} data-testid="tax-inclusive-toggle" />
                    </div>
                    <div className="flex items-center justify-between gap-3">
                        <Label className="text-xs text-muted-foreground">Delivery Charge (₹)</Label>
                        <Input type="number" value={deliveryCharge} onChange={(e) => setDeliveryCharge(e.target.value)} className="h-8 w-28 text-right num" data-testid="totals-delivery-charge" />
                    </div>
                    <div className="flex items-center justify-between gap-3">
                        <Label className="text-xs text-muted-foreground">Packaging (₹)</Label>
                        <Input type="number" value={packagingCharge} onChange={(e) => setPackagingCharge(e.target.value)} className="h-8 w-28 text-right num" data-testid="totals-packaging-charge" />
                    </div>
                    <div className="flex items-center justify-between gap-3">
                        <Label className="text-xs text-muted-foreground">Delivery Charge (₹)</Label>
                        <Input type="number" value={deliveryCharge} onChange={(e) => setDeliveryCharge(e.target.value)} placeholder="0" className="h-8 w-28 text-right num" data-testid="delivery-charge" />
                    </div>
                    <div className="flex items-center justify-between gap-3">
                        <Label className="text-xs text-muted-foreground">Packaging Charge (₹)</Label>
                        <Input type="number" value={packagingCharge} onChange={(e) => setPackagingCharge(e.target.value)} placeholder="0" className="h-8 w-28 text-right num" data-testid="packaging-charge" />
                    </div>
                    <div className="flex items-center justify-between gap-3">
                        <Label className="text-xs text-muted-foreground">Extra Discount (₹)</Label>
                        <Input type="number" value={extraDiscount} onChange={(e) => setExtraDiscount(e.target.value)} className="h-8 w-28 text-right num" data-testid="extra-discount" />
                    </div>
                    <div className="flex items-center justify-between gap-3">
                        <Label className="text-xs text-muted-foreground">Adjustment ± (₹)</Label>
                        <Input type="number" value={adjustment} onChange={(e) => setAdjustment(e.target.value)} placeholder="e.g. -50 or 100" className="h-8 w-28 text-right num" data-testid="invoice-adjustment" />
                    </div>

                    {/* More Charges — collapsible */}
                    <details className="rounded-md border border-border bg-muted/30" data-testid="more-charges-section">
                        <summary className="cursor-pointer px-3 py-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground select-none flex items-center justify-between">
                            <span>+ More Charges</span>
                            {totals.chargesTotal > 0 && <span className="text-primary num font-bold">{formatINR(totals.chargesTotal)}</span>}
                        </summary>
                        <div className="p-3 pt-1 space-y-1.5">
                            {[
                                { key: "loading", label: "Loading" },
                                { key: "unloading", label: "Unloading" },
                                { key: "freight", label: "Freight" },
                                { key: "insurance", label: "Insurance" },
                                { key: "labour", label: "Labour" },
                                { key: "other", label: "Other" },
                            ].map((c) => (
                                <div key={c.key} className="flex items-center justify-between gap-2">
                                    <Label className="text-[11px] text-muted-foreground">{c.label} (₹)</Label>
                                    <Input
                                        type="number"
                                        value={charges[c.key]}
                                        onChange={(e) => setCharges((p) => ({ ...p, [c.key]: e.target.value }))}
                                        className="h-7 w-24 text-right num text-xs"
                                        data-testid={`charge-${c.key}`}
                                    />
                                </div>
                            ))}
                        </div>
                    </details>

                    <div className="flex items-center justify-between gap-2">
                        <Label className="text-xs text-muted-foreground flex items-center gap-2">
                            Auto Round Off
                            <Switch checked={autoRoundOff} onCheckedChange={setAutoRoundOff} data-testid="auto-roundoff-toggle" />
                        </Label>
                        {autoRoundOff ? (
                            <Select value={roundOffMode} onValueChange={setRoundOffMode}>
                                <SelectTrigger className="h-8 w-32 text-xs" data-testid="round-off-mode">
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem value="nearest_rupee">Nearest ₹1</SelectItem>
                                    <SelectItem value="nearest_50p">Nearest 50p</SelectItem>
                                </SelectContent>
                            </Select>
                        ) : (
                            <Input type="number" step="0.01" value={roundOff} onChange={(e) => setRoundOff(e.target.value)} className="h-8 w-24 text-right num" data-testid="round-off" />
                        )}
                    </div>
                    {autoRoundOff && (
                        <div className="flex items-center justify-between text-[10px] text-muted-foreground -mt-1">
                            <span>Round-off effective</span>
                            <span className="num">{formatINR(totals.roundOffEffective)}</span>
                        </div>
                    )}
                    <div className="border-t border-border pt-3 mt-2 flex items-center justify-between">
                        <span className="label-cap">{t("inv.grandTotal")}</span>
                        <span className="font-display text-2xl font-bold num text-primary">{formatINR(totals.total)}</span>
                    </div>
                    <div className="flex items-center justify-between gap-3 pt-2">
                        <Label className="text-xs text-muted-foreground">{t("inv.received")} (₹)</Label>
                        <Input type="number" value={paymentReceived} onChange={(e) => setPaymentReceived(e.target.value)} className="h-8 w-28 text-right num" data-testid="payment-received" />
                    </div>
                    <div className="flex items-center justify-between text-xs">
                        <span className="text-muted-foreground">{t("inv.balance")}</span>
                        <span className="num font-semibold text-amber-600 dark:text-amber-400">{formatINR(Math.max(0, totals.total - (Number(paymentReceived) || 0)))}</span>
                    </div>

                    {/* Scan Bill + Voice-to-Invoice — moved here from top so they sit
                        right above the Save button (Vyapar/Tally-style quick actions).
                        Reuses the existing BillScanUpload component verbatim — no logic changes. */}
                    {(mode === "sale" || mode === "purchase") && (
                        <div className="pt-3 border-t border-border space-y-2 animate-in fade-in slide-in-from-bottom-1 duration-300">
                            <BillScanUpload kind={mode} onExtracted={applyOcr} dataTestidPrefix={`${mode}-bill-scan`} />
                            <button
                                type="button"
                                onClick={() => setVoiceOpen(true)}
                                className="w-full flex items-center gap-2.5 px-3 py-2.5 rounded-lg border border-amber-500/40 bg-gradient-to-r from-amber-50/60 to-emerald-50/40 dark:from-amber-950/30 dark:to-emerald-950/20 hover:from-amber-100/70 hover:to-emerald-100/50 dark:hover:from-amber-950/50 transition-all text-left"
                                data-testid="open-voice-to-invoice"
                                aria-label={`Open Voice-to-${mode === "sale" ? "Invoice" : "Purchase"}`}
                            >
                                <div className="h-8 w-8 rounded-lg bg-gradient-to-br from-amber-400 to-amber-600 text-amber-950 flex items-center justify-center flex-shrink-0">
                                    <Sparkles className="h-4 w-4" strokeWidth={2.4} />
                                </div>
                                <div className="min-w-0 flex-1">
                                    <div className="text-xs font-semibold leading-tight flex items-center gap-1">
                                        Voice-to-{mode === "sale" ? "Invoice" : "Purchase"}
                                        <Badge variant="outline" className="text-[9px] py-0 px-1">AI</Badge>
                                    </div>
                                    <div className="text-[10px] text-muted-foreground leading-tight mt-0.5">Bolo aur AI form bhar dega</div>
                                </div>
                                <Sparkles className="h-3.5 w-3.5 text-amber-500 flex-shrink-0" />
                            </button>
                        </div>
                    )}

                    <Button onClick={() => submit({ andThen: "none" })} disabled={busy} className="w-full mt-3 bg-primary hover:bg-primary/90 h-11" data-testid="save-invoice-button">
                        <Sparkles className="h-4 w-4 mr-1.5" />
                        {busy ? "Saving…" : `${isEdit ? "Update" : t("common.save")} ${mode === "quotation" ? "Quotation" : "Invoice"}`}
                    </Button>

                    {/* Smart Save quick-action row */}
                    {!isEdit && (
                        <div className="grid grid-cols-4 gap-1.5 mt-2" data-testid="smart-save-row">
                            <Button variant="outline" size="sm" disabled={busy} onClick={() => submit({ andThen: "print" })} title="Save & Print" className="h-9 text-[11px]" data-testid="save-and-print">
                                <Printer className="h-3.5 w-3.5 mr-1" /> Print
                            </Button>
                            <Button variant="outline" size="sm" disabled={busy} onClick={() => submit({ andThen: "share" })} title="Save & Share PDF" className="h-9 text-[11px]" data-testid="save-and-share">
                                <Share2 className="h-3.5 w-3.5 mr-1" /> Share
                            </Button>
                            <Button variant="outline" size="sm" disabled={busy} onClick={() => submit({ andThen: "whatsapp" })} title="Save & Send on WhatsApp" className="h-9 text-[11px] border-emerald-500/40 text-emerald-700" data-testid="save-and-whatsapp">
                                <MessageCircle className="h-3.5 w-3.5 mr-1" /> WA
                            </Button>
                            <Button variant="outline" size="sm" disabled={busy} onClick={() => submit({ andThen: "email" })} title="Save & Email" className="h-9 text-[11px]" data-testid="save-and-email">
                                <Send className="h-3.5 w-3.5 mr-1" /> Email
                            </Button>
                        </div>
                    )}
                </CardContent></Card>
            </div>

            {/* Vyapar-parity: inline Add Party modal — launched from PartyPicker */}
            <QuickAddPartyModal
                open={quickAddOpen}
                onClose={() => setQuickAddOpen(false)}
                onCreated={handlePartyCreated}
                companyId={activeId}
                initialName={quickAddSeed}
                defaultType={mode === "purchase" ? "vendor" : "customer"}
            />

            {/* v12 Vyapar-parity: inline Add Item modal — launched from ItemPicker when search yields no matches */}
            <QuickAddItemModal
                open={quickAddItemOpen}
                onClose={() => { setQuickAddItemOpen(false); setQuickAddItemLineIdx(null); }}
                onCreated={handleItemCreated}
                companyId={activeId}
                initialName={quickAddItemSeed}
            />
        </div>
    );
}
