/**
 * InvoiceList — Vyapar-style sales/purchase/etc. transaction list.
 *
 * Layout (top → bottom):
 *   1. Page header + "New" CTA
 *   2. 5 dashboard tiles: Total Sales | Received | Outstanding | Loyalty Awarded | Records
 *   3. Advanced filter bar (search + date range + party + payment mode + status)
 *   4. Sortable table with payment type, balance, due date columns
 *   5. Per-row dropdown action menu (view, edit, e-invoice, duplicate, etc.)
 *
 * NOTE on what's wired vs stubbed:
 *   • View / Print / Delete           — fully wired
 *   • Convert to Sale Invoice         — wired for quotation/challan/order/proforma
 *   • Duplicate                       — wired (POSTs the same body to /api/invoices)
 *   • E-Invoice / Cancel / Return     — UI present, backend stubs emit toasts so the
 *                                       menu structure is in place. Easy to flesh out
 *                                       later without touching this file.
 */
import React, { useEffect, useState, useCallback, useMemo } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Checkbox } from "@/components/ui/checkbox";
import {
    DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem,
    DropdownMenuLabel, DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
import {
    Plus, Search, Eye, Trash2, Printer, ArrowRightLeft, MoreVertical,
    Copy, FileText, Receipt, History, XCircle, RefreshCw,
    Filter, BadgeIndianRupee, Wallet, AlertCircle, Gift,
    ChevronDown, X, Pencil, CheckSquare, Square, Loader2,
} from "lucide-react";
import { api, formatApiError } from "@/lib/api";
import { useCompany } from "@/context/CompanyContext";
import { useAuth } from "@/context/AuthContext";
import { usePermissions } from "@/context/PermissionsContext";
import { formatINR, formatDate } from "@/lib/format";
import { toast } from "sonner";
import { safeDelete } from "@/lib/safeDelete";
import { makeShareDraggable } from "@/lib/shareDrag";

const CONVERTIBLE = new Set(["quotation", "challan", "sale_order", "proforma"]);
const PAYMENT_MODES = ["all", "cash", "upi", "card", "bank", "cheque", "credit"];

function StatusBadge({ status }) {
    const map = {
        paid:    { cls: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/30", label: "Paid" },
        partial: { cls: "bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/30", label: "Partial" },
        unpaid:  { cls: "bg-rose-500/15 text-rose-700 dark:text-rose-400 border-rose-500/30", label: "Unpaid" },
    };
    const s = map[status] || map.unpaid;
    return <span className={`inline-flex items-center px-2 py-0.5 rounded text-[10px] uppercase tracking-wider border ${s.cls}`}>{s.label}</span>;
}

function Tile({ label, value, icon: Icon, color = "text-primary", testid }) {
    return (
        <Card data-testid={testid}>
            <CardContent className="p-4 flex items-center justify-between gap-3">
                <div className="min-w-0">
                    <div className="label-cap whitespace-nowrap truncate">{label}</div>
                    <div className={`mt-1 font-display text-xl sm:text-2xl font-bold num ${color}`}>{value}</div>
                </div>
                {Icon && (
                    <div className={`h-9 w-9 rounded-lg flex items-center justify-center flex-shrink-0 bg-muted/50 ${color}`}>
                        <Icon className="h-4 w-4" />
                    </div>
                )}
            </CardContent>
        </Card>
    );
}

export function InvoiceList({ type, title, subtitle, newPath, viewPath }) {
    const { activeId } = useCompany();
    const { isAdmin } = useAuth();
    const { can } = usePermissions();
    const navigate = useNavigate();
    const [rows, setRows] = useState([]);
    const [loading, setLoading] = useState(true);

    // Filters
    const [q, setQ] = useState("");
    const [dateFrom, setDateFrom] = useState("");
    const [dateTo, setDateTo] = useState("");
    const [partyFilter, setPartyFilter] = useState("");
    const [payModeFilter, setPayModeFilter] = useState("all");
    const [statusFilter, setStatusFilter] = useState("all");
    const [showFilters, setShowFilters] = useState(false);

    // Bulk-select state — permission-gated by `invoicing.delete`
    const canDelete = can("invoicing.delete");
    const [selectMode, setSelectMode] = useState(false);
    const [selectedIds, setSelectedIds] = useState(() => new Set());
    const [bulkDeleting, setBulkDeleting] = useState(false);

    const load = useCallback(async () => {
        if (!activeId) return;
        setLoading(true);
        try {
            const { data } = await api.get("/invoices", { params: { company_id: activeId, type } });
            setRows(data || []);
        } catch (e) {
            if (!e.isNetworkError) toast.error(formatApiError(e.response?.data?.detail) || "Failed to load");
        } finally { setLoading(false); }
    }, [activeId, type]);
    useEffect(() => { load(); }, [load]);

    // -------- filtering --------
    const filtered = useMemo(() => {
        const term = q.trim().toLowerCase();
        const party = partyFilter.trim().toLowerCase();
        return rows.filter((r) => {
            if (term && !(
                (r.invoice_no || "").toLowerCase().includes(term) ||
                (r.party_name || "").toLowerCase().includes(term)
            )) return false;
            if (party && !(r.party_name || "").toLowerCase().includes(party)) return false;
            if (dateFrom && r.invoice_date && r.invoice_date < dateFrom) return false;
            if (dateTo && r.invoice_date && r.invoice_date > dateTo) return false;
            if (payModeFilter !== "all" && (r.payment_mode || "").toLowerCase() !== payModeFilter) return false;
            if (statusFilter !== "all" && (r.status || "unpaid") !== statusFilter) return false;
            return true;
        });
    }, [rows, q, partyFilter, dateFrom, dateTo, payModeFilter, statusFilter]);

    // -------- totals for tiles --------
    const stats = useMemo(() => {
        let total = 0, received = 0, outstanding = 0, loyalty = 0, discount = 0;
        for (const r of filtered) {
            total += Number(r.total || 0);
            received += Number(r.payment_received || 0);
            outstanding += Math.max(0, Number(r.total || 0) - Number(r.payment_received || 0));
            loyalty += Number(r.loyalty_awarded || 0);
            discount += Number(r.discount_redeemed || 0);
        }
        return { total, received, outstanding, loyalty, discount };
    }, [filtered]);

    const hasActiveFilter = dateFrom || dateTo || partyFilter || payModeFilter !== "all" || statusFilter !== "all";

    const clearFilters = () => {
        setDateFrom(""); setDateTo(""); setPartyFilter("");
        setPayModeFilter("all"); setStatusFilter("all");
    };

    // -------- actions --------
    const remove = async (row) => {
        await safeDelete("invoice", row, { onSuccess: load });
    };

    // -------- bulk select / delete --------
    const visibleIds = useMemo(() => filtered.map((r) => r.id), [filtered]);
    const allVisibleSelected = visibleIds.length > 0 && visibleIds.every((id) => selectedIds.has(id));
    const someVisibleSelected = !allVisibleSelected && visibleIds.some((id) => selectedIds.has(id));

    const toggleOne = useCallback((id) => {
        setSelectedIds((prev) => {
            const next = new Set(prev);
            if (next.has(id)) next.delete(id); else next.add(id);
            return next;
        });
    }, []);

    const toggleAll = useCallback(() => {
        setSelectedIds((prev) => {
            const next = new Set(prev);
            if (allVisibleSelected) {
                for (const id of visibleIds) next.delete(id);
            } else {
                for (const id of visibleIds) next.add(id);
            }
            return next;
        });
    }, [visibleIds, allVisibleSelected]);

    const clearSelection = useCallback(() => setSelectedIds(new Set()), []);

    const exitSelectMode = useCallback(() => {
        setSelectMode(false);
        setSelectedIds(new Set());
    }, []);

    const bulkDelete = useCallback(async () => {
        if (!canDelete) {
            toast.error("You don't have permission to delete invoices.");
            return;
        }
        const ids = Array.from(selectedIds);
        if (ids.length === 0) {
            toast.info("Select at least one invoice first.");
            return;
        }
        if (!window.confirm(`Delete ${ids.length} invoice${ids.length > 1 ? "s" : ""}? They will be moved to Trash.`)) return;
        setBulkDeleting(true);
        try {
            const { data } = await api.post("/invoices/bulk-delete", { ids });
            toast.success(`Moved ${data.moved} invoice${data.moved !== 1 ? "s" : ""} to Trash${data.skipped ? ` · ${data.skipped} skipped` : ""}`);
            setSelectedIds(new Set());
            setSelectMode(false);
            await load();
        } catch (e) {
            toast.error(formatApiError(e.response?.data?.detail) || "Bulk delete failed");
        } finally {
            setBulkDeleting(false);
        }
    }, [canDelete, selectedIds, load]);

    const convert = async (id) => {
        if (!window.confirm("Convert into a Sale Invoice?")) return;
        try {
            const { data } = await api.post(`/invoices/${id}/convert-to-invoice`);
            toast.success(`Invoice ${data.invoice_no} created`);
            navigate(`/sales/${data.id}`);
        } catch (e) { toast.error(formatApiError(e.response?.data?.detail)); }
    };

    const duplicate = async (id) => {
        // Pull the full invoice → POST a new one with the same body (without _id)
        try {
            const { data: orig } = await api.get(`/invoices/${id}`);
            const body = { ...orig };
            delete body.id; delete body._id; delete body.invoice_no;
            delete body.created_at; delete body.updated_at; delete body.status;
            body.invoice_date = new Date().toISOString().slice(0, 10);
            body.payment_received = 0;
            const { data: dup } = await api.post(`/invoices?company_id=${activeId}`, body);
            toast.success(`Duplicated as ${dup.invoice_no}`);
            load();
        } catch (e) { toast.error(formatApiError(e.response?.data?.detail) || "Duplicate failed"); }
    };

    const openPdf = (id) => window.open(`${process.env.REACT_APP_BACKEND_URL}/api/invoices/${id}/pdf`, "_blank");
    const print = (id) => window.open(`${viewPath}/${id}?print=1`, "_blank");
    const preview = (id) => navigate(`${viewPath}/${id}`);
    const editInvoice = (id) => navigate(`${viewPath}/${id}/edit`);
    const paymentHistory = (id) => navigate(`${viewPath}/${id}?tab=payments`);
    const activityHistory = (id) => navigate(`${viewPath}/${id}?tab=history`);
    const generateEInvoice = (id) => toast.info("E-Invoice generation coming soon — JSON export is available from the invoice view page.");
    const convertToReturn = async (id) => {
        if (!window.confirm("Create a sale return (credit note) for this invoice?")) return;
        toast.info("Sale return conversion will be added in the next release — for now use Credit Note → New.");
    };
    const cancelInvoice = async (id) => {
        if (!window.confirm("Cancel this invoice? It will be marked as cancelled but kept for audit.")) return;
        try {
            await api.patch(`/invoices/${id}`, { status: "cancelled" });
            toast.success("Invoice cancelled");
            load();
        } catch (e) { toast.error(formatApiError(e.response?.data?.detail) || "Cancel failed"); }
    };

    return (
        <div className="space-y-5" data-testid={`${type}-page`}>
            {/* Header */}
            <div className="flex flex-wrap items-end justify-between gap-3">
                <div>
                    <div className="label-cap">Transactions</div>
                    <h1 className="font-display text-3xl font-bold tracking-tight">{title}</h1>
                    <p className="text-sm text-muted-foreground mt-1">{subtitle}</p>
                </div>
                <div className="flex flex-wrap items-center gap-2">
                    {/* Select toggle — entering "select mode" reveals checkboxes */}
                    <Button
                        variant={selectMode ? "default" : "outline"}
                        onClick={() => (selectMode ? exitSelectMode() : setSelectMode(true))}
                        data-testid={`${type}-select-toggle`}
                        title={selectMode ? "Exit select mode" : "Select rows for bulk actions"}
                    >
                        {selectMode ? <CheckSquare className="h-3.5 w-3.5 mr-1" /> : <Square className="h-3.5 w-3.5 mr-1" />}
                        {selectMode ? `Selected (${selectedIds.size})` : "Select"}
                    </Button>

                    {/* Delete — visible always but disabled until user has permission AND something is selected */}
                    <Button
                        variant="outline"
                        className="border-destructive/40 text-destructive hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
                        onClick={bulkDelete}
                        disabled={!canDelete || selectedIds.size === 0 || bulkDeleting}
                        title={!canDelete ? "You do not have permission to delete invoices" : selectedIds.size === 0 ? "Select rows first" : `Delete ${selectedIds.size} invoice(s)`}
                        data-testid={`${type}-bulk-delete-btn`}
                    >
                        {bulkDeleting
                            ? <Loader2 className="h-3.5 w-3.5 mr-1 animate-spin" />
                            : <Trash2 className="h-3.5 w-3.5 mr-1" />}
                        Delete{selectedIds.size > 0 ? ` (${selectedIds.size})` : ""}
                    </Button>

                    <Button variant="outline" onClick={load} disabled={loading} data-testid={`${type}-refresh`}>
                        <RefreshCw className={`h-3.5 w-3.5 mr-1 ${loading ? "animate-spin" : ""}`} /> Refresh
                    </Button>
                    <Button asChild className="bg-primary hover:bg-primary/90" data-testid={`new-${type}-button`}>
                        <Link to={newPath}><Plus className="h-4 w-4 mr-1.5" /> New</Link>
                    </Button>
                </div>
            </div>

            {/* Dashboard tiles */}
            <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3">
                <Tile label="Total Value" value={formatINR(stats.total)} icon={BadgeIndianRupee} color="text-primary" testid={`${type}-tile-total`} />
                <Tile label="Received" value={formatINR(stats.received)} icon={Wallet} color="text-emerald-600 dark:text-emerald-400" testid={`${type}-tile-received`} />
                <Tile label="Outstanding" value={formatINR(stats.outstanding)} icon={AlertCircle} color="text-rose-600 dark:text-rose-400" testid={`${type}-tile-outstanding`} />
                <Tile label="Loyalty Pts" value={stats.loyalty.toLocaleString("en-IN")} icon={Gift} color="text-fuchsia-600 dark:text-fuchsia-400" testid={`${type}-tile-loyalty`} />
                <Tile label="Records" value={filtered.length} icon={FileText} color="text-blue-600 dark:text-blue-400" testid={`${type}-tile-count`} />
            </div>

            {/* Filter + table */}
            <Card>
                <CardContent className="p-0">
                    {/* Filter bar */}
                    <div className="border-b border-border p-3 space-y-2">
                        <div className="flex items-center gap-2 flex-wrap">
                            <div className="relative flex-1 min-w-[200px] max-w-md">
                                <Search className="h-4 w-4 absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground" />
                                <Input
                                    placeholder="Search invoice no, party…"
                                    value={q}
                                    onChange={(e) => setQ(e.target.value)}
                                    className="pl-8 h-9"
                                    data-testid={`${type}-search`}
                                />
                            </div>
                            <Button
                                variant={showFilters || hasActiveFilter ? "default" : "outline"}
                                size="sm"
                                onClick={() => setShowFilters((v) => !v)}
                                data-testid={`${type}-filters-toggle`}
                            >
                                <Filter className="h-3.5 w-3.5 mr-1" />
                                Filters {hasActiveFilter && <Badge className="ml-1 h-4 px-1 text-[10px]">on</Badge>}
                                <ChevronDown className={`h-3.5 w-3.5 ml-1 transition-transform ${showFilters ? "rotate-180" : ""}`} />
                            </Button>
                            {hasActiveFilter && (
                                <Button variant="ghost" size="sm" onClick={clearFilters} data-testid={`${type}-filters-clear`}>
                                    <X className="h-3.5 w-3.5 mr-1" /> Clear
                                </Button>
                            )}
                        </div>
                        {showFilters && (
                            <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-2 pt-1" data-testid={`${type}-filter-panel`}>
                                <div>
                                    <label className="text-[10px] font-semibold uppercase text-muted-foreground tracking-wider">From</label>
                                    <Input type="date" value={dateFrom} onChange={(e) => setDateFrom(e.target.value)} className="h-8" data-testid={`${type}-filter-from`} />
                                </div>
                                <div>
                                    <label className="text-[10px] font-semibold uppercase text-muted-foreground tracking-wider">To</label>
                                    <Input type="date" value={dateTo} onChange={(e) => setDateTo(e.target.value)} className="h-8" data-testid={`${type}-filter-to`} />
                                </div>
                                <div>
                                    <label className="text-[10px] font-semibold uppercase text-muted-foreground tracking-wider">Party</label>
                                    <Input value={partyFilter} onChange={(e) => setPartyFilter(e.target.value)} placeholder="Party name…" className="h-8" data-testid={`${type}-filter-party`} />
                                </div>
                                <div>
                                    <label className="text-[10px] font-semibold uppercase text-muted-foreground tracking-wider">Payment Mode</label>
                                    <select
                                        value={payModeFilter}
                                        onChange={(e) => setPayModeFilter(e.target.value)}
                                        className="h-8 w-full rounded-md border bg-background px-2 text-sm capitalize"
                                        data-testid={`${type}-filter-mode`}
                                    >
                                        {PAYMENT_MODES.map((m) => <option key={m} value={m} className="capitalize">{m}</option>)}
                                    </select>
                                </div>
                                <div>
                                    <label className="text-[10px] font-semibold uppercase text-muted-foreground tracking-wider">Status</label>
                                    <select
                                        value={statusFilter}
                                        onChange={(e) => setStatusFilter(e.target.value)}
                                        className="h-8 w-full rounded-md border bg-background px-2 text-sm capitalize"
                                        data-testid={`${type}-filter-status`}
                                    >
                                        <option value="all">All</option>
                                        <option value="paid">Paid</option>
                                        <option value="partial">Partial</option>
                                        <option value="unpaid">Unpaid</option>
                                        <option value="cancelled">Cancelled</option>
                                    </select>
                                </div>
                            </div>
                        )}
                    </div>

                    {/* Table */}
                    <div className="overflow-x-auto">
                        <table className="w-full dense-table text-sm" data-testid={`${type}-table`}>
                            <thead className="bg-muted/40">
                                <tr className="text-left text-[10px] tracking-wider uppercase text-muted-foreground">
                                    {selectMode && (
                                        <th className="px-3 py-2.5 w-10">
                                            <Checkbox
                                                checked={allVisibleSelected ? true : (someVisibleSelected ? "indeterminate" : false)}
                                                onCheckedChange={toggleAll}
                                                aria-label="Select all visible"
                                                data-testid={`${type}-select-all`}
                                            />
                                        </th>
                                    )}
                                    <th className="px-5 py-2.5">Invoice No.</th>
                                    <th>Date</th>
                                    <th>Party</th>
                                    <th>Type</th>
                                    <th>Payment</th>
                                    <th className="text-right">Total</th>
                                    <th className="text-right">Paid</th>
                                    <th className="text-right">Balance</th>
                                    <th className="text-right">Loyalty</th>
                                    <th>Due</th>
                                    <th>Status</th>
                                    <th className="text-right pr-5">Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {loading ? (
                                    <tr><td colSpan={selectMode ? 13 : 12} className="p-8 text-center text-muted-foreground">Loading…</td></tr>
                                ) : filtered.length === 0 ? (
                                    <tr>
                                        <td colSpan={selectMode ? 13 : 12} className="p-12 text-center text-muted-foreground">
                                            {rows.length === 0
                                                ? <>No transactions. <Link to={newPath} className="text-primary underline">Create your first one</Link>.</>
                                                : <>No results for the current filters. <button onClick={clearFilters} className="text-primary underline">Clear filters</button>.</>
                                            }
                                        </td>
                                    </tr>
                                ) : filtered.map((r) => {
                                    const balance = Math.max(0, Number(r.total || 0) - Number(r.payment_received || 0));
                                    const isSelected = selectedIds.has(r.id);
                                    return (
                                        <tr
                                            key={r.id}
                                            className={`border-t border-border hover:bg-muted/20 ${isSelected ? "bg-primary/5" : ""}`}
                                            data-testid={`${type}-row-${r.id}`}
                                            title="Tip: drag this invoice onto the WhatsApp button to share"
                                            {...makeShareDraggable("invoice", {
                                                id: r.id,
                                                invoice_no: r.invoice_no,
                                                invoice_date: r.invoice_date || r.date,
                                                party_id: r.party_id,
                                                party_name: r.party_name,
                                                party_phone: r.party_phone,
                                                total: r.total,
                                                payment_received: r.payment_received,
                                                type: r.type,
                                                status: r.status,
                                            })}
                                        >
                                            {selectMode && (
                                                <td className="px-3 align-middle">
                                                    <Checkbox
                                                        checked={isSelected}
                                                        onCheckedChange={() => toggleOne(r.id)}
                                                        aria-label={`Select invoice ${r.invoice_no}`}
                                                        data-testid={`${type}-select-${r.id}`}
                                                    />
                                                </td>
                                            )}
                                            <td className="px-5 font-mono text-xs font-medium">{r.invoice_no}</td>
                                            <td className="text-muted-foreground text-xs whitespace-nowrap">{formatDate(r.invoice_date)}</td>
                                            <td className="max-w-[160px] truncate">{r.party_name || "Walk-in"}</td>
                                            <td className="text-xs capitalize text-muted-foreground">{type}</td>
                                            <td className="text-xs capitalize">{r.payment_mode || "—"}</td>
                                            <td className="text-right num font-semibold">{formatINR(r.total)}</td>
                                            <td className="text-right num text-emerald-600 dark:text-emerald-400">{formatINR(r.payment_received)}</td>
                                            <td className={`text-right num ${balance > 0 ? "text-amber-600 dark:text-amber-400 font-semibold" : "text-muted-foreground"}`}>{formatINR(balance)}</td>
                                            <td className="text-right num text-fuchsia-600 dark:text-fuchsia-400">{r.loyalty_awarded || 0}</td>
                                            <td className="text-muted-foreground text-xs whitespace-nowrap">{r.due_date ? formatDate(r.due_date) : "—"}</td>
                                            <td><StatusBadge status={r.status} /></td>
                                            <td className="text-right pr-5 whitespace-nowrap">
                                                {CONVERTIBLE.has(type) && (
                                                    <Button size="sm" className="h-7 mr-1 bg-primary hover:bg-primary/90" onClick={() => convert(r.id)} data-testid={`convert-${type}-${r.id}`}>
                                                        <ArrowRightLeft className="h-3.5 w-3.5 mr-1" /> Convert
                                                    </Button>
                                                )}
                                                <DropdownMenu>
                                                    <DropdownMenuTrigger asChild>
                                                        <Button size="icon" variant="ghost" className="h-8 w-8" data-testid={`actions-${type}-${r.id}`}>
                                                            <MoreVertical className="h-4 w-4" />
                                                        </Button>
                                                    </DropdownMenuTrigger>
                                                    <DropdownMenuContent align="end" className="w-56">
                                                        <DropdownMenuLabel>Actions</DropdownMenuLabel>
                                                        <DropdownMenuItem onClick={() => preview(r.id)} data-testid={`act-view-${r.id}`}>
                                                            <Eye className="h-4 w-4 mr-2" /> View Invoice
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => editInvoice(r.id)} disabled={r.status === "cancelled"} data-testid={`act-edit-${r.id}`}>
                                                            <Pencil className="h-4 w-4 mr-2" /> Edit Invoice
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => print(r.id)} data-testid={`act-print-${r.id}`}>
                                                            <Printer className="h-4 w-4 mr-2" /> Print Invoice
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => openPdf(r.id)} data-testid={`act-pdf-${r.id}`}>
                                                            <FileText className="h-4 w-4 mr-2" /> Open PDF
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => duplicate(r.id)} data-testid={`act-duplicate-${r.id}`}>
                                                            <Copy className="h-4 w-4 mr-2" /> Duplicate Invoice
                                                        </DropdownMenuItem>
                                                        <DropdownMenuSeparator />
                                                        <DropdownMenuItem onClick={() => generateEInvoice(r.id)} data-testid={`act-einv-${r.id}`}>
                                                            <Receipt className="h-4 w-4 mr-2" /> Generate E-Invoice
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => convertToReturn(r.id)} disabled={type !== "sale"} data-testid={`act-return-${r.id}`}>
                                                            <ArrowRightLeft className="h-4 w-4 mr-2" /> Convert to Return
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => paymentHistory(r.id)} data-testid={`act-payments-${r.id}`}>
                                                            <Wallet className="h-4 w-4 mr-2" /> Payment History
                                                        </DropdownMenuItem>
                                                        <DropdownMenuItem onClick={() => activityHistory(r.id)} data-testid={`act-activity-${r.id}`}>
                                                            <History className="h-4 w-4 mr-2" /> Activity History
                                                        </DropdownMenuItem>
                                                        {type === "purchase" && (
                                                            <DropdownMenuItem onClick={() => navigate(`/grns/new?from=${r.id}`)} data-testid={`act-grn-${r.id}`}>
                                                                <FileText className="h-4 w-4 mr-2 text-teal-600" /> Create GRN (Goods Received)
                                                            </DropdownMenuItem>
                                                        )}
                                                        <DropdownMenuSeparator />
                                                        {isAdmin && r.status !== "cancelled" && (
                                                            <DropdownMenuItem onClick={() => cancelInvoice(r.id)} className="text-amber-600 focus:text-amber-700" data-testid={`act-cancel-${r.id}`}>
                                                                <XCircle className="h-4 w-4 mr-2" /> Cancel Invoice
                                                            </DropdownMenuItem>
                                                        )}
                                                        {canDelete && (
                                                            <DropdownMenuItem onClick={() => remove(r)} className="text-destructive focus:text-destructive" data-testid={`act-delete-${r.id}`}>
                                                                <Trash2 className="h-4 w-4 mr-2" /> Delete Invoice
                                                            </DropdownMenuItem>
                                                        )}
                                                    </DropdownMenuContent>
                                                </DropdownMenu>
                                            </td>
                                        </tr>
                                    );
                                })}
                            </tbody>
                        </table>
                    </div>
                </CardContent>
            </Card>

            {/* Floating bulk-action bar — visible only when something is selected */}
            {selectMode && selectedIds.size > 0 && (
                <div
                    className="fixed bottom-4 left-1/2 -translate-x-1/2 z-30 flex items-center gap-2 rounded-xl bg-background/95 backdrop-blur border border-border shadow-2xl shadow-black/10 px-3 py-2"
                    role="region"
                    aria-label="Bulk actions"
                    data-testid={`${type}-bulk-actionbar`}
                >
                    <span className="text-sm font-medium px-2" data-testid={`${type}-bulk-count`}>
                        {selectedIds.size} selected
                    </span>
                    <Button
                        variant="ghost"
                        size="sm"
                        onClick={clearSelection}
                        data-testid={`${type}-bulk-clear`}
                    >
                        Clear
                    </Button>
                    <Button
                        size="sm"
                        variant="destructive"
                        onClick={bulkDelete}
                        disabled={!canDelete || bulkDeleting}
                        title={!canDelete ? "No permission" : "Move selected to Trash"}
                        data-testid={`${type}-bulk-delete-floating`}
                    >
                        {bulkDeleting
                            ? <Loader2 className="h-3.5 w-3.5 mr-1 animate-spin" />
                            : <Trash2 className="h-3.5 w-3.5 mr-1" />}
                        Delete
                    </Button>
                    <Button
                        variant="ghost"
                        size="sm"
                        onClick={exitSelectMode}
                        data-testid={`${type}-bulk-exit`}
                    >
                        <X className="h-3.5 w-3.5" />
                    </Button>
                </div>
            )}
        </div>
    );
}
