import { useEffect, useMemo, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useQueryStates } from "nuqs";
import { useQueryClient } from "@tanstack/react-query";
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  type ColumnDef,
} from "@tanstack/react-table";
import {
  ArrowDown,
  ArrowUp,
  ArrowUpDown,
  ChevronLeft,
  ChevronRight,
  Inbox,
  MoreHorizontal,
  Plus,
  RefreshCw,
  Search,
  TriangleAlert,
  X,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import StatusBadge from "./StatusBadge";
import FormModal from "./FormModal";
import { TableSkeleton } from "./Skeleton";
import { useRVScreen, useRecordViewData } from "@/hooks/useRecordView";
import { tableParams, tableParamsOptions, DENSITY_CELL, type Density } from "@/lib/tableParams";
import { formatCurrency, formatDate, formatTime, isMoneyField } from "@/lib/format";
import { colorForName } from "@/lib/palette";
import { actionsForStateName, type StateAction } from "@/lib/workflow";
import type { RecordViewField, SearchQuery } from "@/api/viewService";

const STATE_FIELD = "current_state_name";
/** Candidate keys for the vendor/company column, best first. */
const VENDOR_KEYS = ["vendor_name", "company_name", "supplier_name", "vendor"];

interface Props {
  viewId: number | string;
  onRowClick?: (instanceId: string) => void;
  toolbarAction?: React.ReactNode;
}

type Row = Record<string, unknown>;

const rowId = (row: Row) => String(row.instance_id ?? row._pk_value ?? "");

export default function RecordViewTable({ viewId, onRowClick, toolbarAction }: Props) {
  const navigate = useNavigate();
  const queryClient = useQueryClient();

  const [params, setParams] = useQueryStates(tableParams, tableParamsOptions);
  const [searchInput, setSearchInput] = useState(params.q);
  const [formModal, setFormModal] = useState<{
    activityId: string;
    instanceId?: string;
    title: string;
  } | null>(null);

  const searchRef = useRef<HTMLInputElement>(null);

  const screen = useRVScreen(viewId);

  const query: SearchQuery = useMemo(
    () => ({
      page: params.page,
      limit: params.limit,
      sort_by: params.sort || undefined,
      sort_dir: params.dir,
      search: params.q || undefined,
      // One entry per selected state. NOTE: this assumes the view-service ORs
      // repeated filters on the same field_key — verify against the live API,
      // since an AND would make any multi-select return zero rows.
      filters: params.status.map((value) => ({
        field_key: STATE_FIELD,
        value,
        data_type: "text",
      })),
    }),
    [params],
  );

  const { data, isPending, isError, error, isFetching, isPlaceholderData, refetch } =
    useRecordViewData(screen.data?.templateId, viewId, query);

  // Reflect an externally-changed search param (command palette, shared link).
  useEffect(() => setSearchInput(params.q), [params.q]);

  // "/" focuses search — this is a keyboard-driven triage queue.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      const el = document.activeElement;
      const typing =
        el instanceof HTMLInputElement ||
        el instanceof HTMLTextAreaElement ||
        (el as HTMLElement | null)?.isContentEditable;
      if (e.key === "/" && !typing) {
        e.preventDefault();
        searchRef.current?.focus();
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  // The served field order puts the status chip in column two, where it
  // dominates the row before the reader knows what the row is. Reorder so the
  // identifying fields lead and the state sits fourth among the data columns.
  const fields = useMemo(() => {
    // The raw instance UUID identifies nothing to a reader — the company does.
    let raw = (data?.fields ?? []).filter((f) => f.field_key !== "instance_id");

    const sample = data?.rows?.[0];
    const vendorKey =
      VENDOR_KEYS.find((k) => raw.some((f) => f.field_key === k)) ??
      (sample ? VENDOR_KEYS.find((k) => k in sample) : undefined);
    // Promote the company to second column, synthesising it when the view
    // serves the value on the row but doesn't list it as a field.
    if (vendorKey) {
      const existing = raw.find((f) => f.field_key === vendorKey);
      const col: RecordViewField =
        existing ?? {
          field_key: vendorKey,
          output_label: "Company",
          data_type: "text",
          is_filter: false,
          is_search: true,
        };
      raw = [
        ...raw.filter((f) => f.field_key !== vendorKey).slice(0, 1),
        col,
        ...raw.filter((f) => f.field_key !== vendorKey).slice(1),
      ];
    }

    const idx = raw.findIndex((f) => f.field_key === STATE_FIELD);
    if (idx === -1) return raw;
    const rest = raw.filter((_, i) => i !== idx);
    const pos = Math.min(3, rest.length);
    return [...rest.slice(0, pos), raw[idx], ...rest.slice(pos)];
  }, [data?.fields, data?.rows]);
  const rows = data?.rows ?? [];

  // Money columns get a footer total. Scoped to the current page and labelled
  // as such — summing only what's on screen is the honest thing a client can do
  // without a server-side aggregate.
  const pageTotals = useMemo(() => {
    const out: Record<string, number> = {};
    for (const f of fields) {
      if (f.data_type !== "number" || !isMoneyField(f.field_key)) continue;
      out[f.field_key] = rows.reduce((sum, r) => sum + (Number(r[f.field_key]) || 0), 0);
    }
    return out;
  }, [fields, rows]);

  const openRow = (row: Row) => {
    const id = rowId(row);
    if (!id) return;
    onRowClick ? onRowClick(id) : navigate(`/detail/${id}`);
  };

  const afterAction = () => {
    setFormModal(null);
    refetch();
    // Tiles and charts aggregate the same underlying view.
    queryClient.invalidateQueries({ queryKey: ["invoice-analytics"] });
  };

  // ── Columns ───────────────────────────────────────────────────────────────
  const columns = useMemo<ColumnDef<Row>[]>(() => {
    if (fields.length === 0) return [];

    const primaryKey = fields.find(
      (f) => f.field_key !== STATE_FIELD && f.field_key !== "instance_id" && f.data_type === "text",
    )?.field_key;

    const dataCols: ColumnDef<Row>[] = fields.map((f) => ({
      id: f.field_key,
      accessorFn: (r) => r[f.field_key],
      header: f.output_label,
      // `numeric` drives right-alignment on the header, the cell and the footer
      // total — figures only line up for comparison when they share an edge.
      meta: { field: f, numeric: f.data_type === "number" },
      cell: ({ getValue }) => renderCell(getValue(), f, f.field_key === primaryKey),
    }));

    const actions: ColumnDef<Row> = {
      id: "__actions",
      size: 72,
      enableHiding: false,
      header: () => <span className="sr-only">Row actions</span>,
      cell: ({ row }) => (
        <RowActions
          row={row.original}
          onAction={(a) =>
            setFormModal({
              activityId: a.activityId,
              instanceId: rowId(row.original),
              title: a.label,
            })
          }
          onOpen={() => openRow(row.original)}
        />
      ),
    };

    return [...dataCols, actions];
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [fields]);

  const table = useReactTable({
    data: rows,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getRowId: (row, i) => rowId(row) || String(i),
    // Sorting and pagination happen server-side; the table is a render model.
    manualSorting: true,
    manualPagination: true,
  });

  // ── Loading / error ───────────────────────────────────────────────────────
  if (screen.isPending || (isPending && !data)) {
    return <TableSkeleton rows={6} cols={6} />;
  }

  if (screen.isError || isError) {
    const message =
      (screen.error as Error)?.message ?? (error as Error)?.message ?? "Something went wrong";
    return (
      <div className="rounded-xl border border-destructive/30 bg-destructive/5 p-10 text-center">
        <TriangleAlert className="mx-auto mb-3 size-5 text-destructive" aria-hidden />
        <p className="mb-3 text-base text-destructive">{message}</p>
        <Button variant="outline" size="sm" onClick={() => refetch()}>
          Try again
        </Button>
      </div>
    );
  }

  const { page, limit, total_count, total_pages } = data!.pagination;
  const from = total_count > 0 ? (page - 1) * limit + 1 : 0;
  const to = Math.min(page * limit, total_count);
  const filtersActive = params.status.length > 0 || !!params.q;

  return (
    <div className="rounded-xl border border-border bg-card">
      {/* ── Toolbar ──────────────────────────────────────────────────────── */}
      <div className="flex flex-wrap items-center gap-x-3 gap-y-2 px-4 py-2.5">
        <div className="flex items-center gap-1.5">
          {/* Search leads the toolbar — it's the control reached for most often,
              with the status filter immediately beside it. */}
          <form
            onSubmit={(e) => {
              e.preventDefault();
              setParams({ q: searchInput, page: 1 });
            }}
            className="relative"
          >
            <Search
              className="pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground"
              aria-hidden
            />
            <Input
              ref={searchRef}
              value={searchInput}
              onChange={(e) => setSearchInput(e.target.value)}
              placeholder="Search…  /"
              aria-label="Search records"
              className="h-8 w-44 pl-8 pr-7"
            />
            {searchInput && (
              <button
                type="button"
                onClick={() => {
                  setSearchInput("");
                  setParams({ q: "", page: 1 });
                }}
                className="absolute right-2 top-1/2 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground"
              >
                <X className="size-3" aria-hidden />
                <span className="sr-only">Clear search</span>
              </button>
            )}
          </form>
        </div>

        <div className="ml-auto flex items-center gap-1.5">
          {screen.data?.startButtons.map((b) => (
            <Button
              key={b.key}
              size="sm"
              onClick={() => setFormModal({ activityId: b.activityId, title: b.label })}
              className="h-8 gap-1.5"
            >
              <Plus className="size-3.5" strokeWidth={2.5} aria-hidden />
              {b.label}
            </Button>
          ))}
          {toolbarAction}

          <Tooltip>
            <TooltipTrigger asChild>
              <Button
                variant="ghost"
                size="icon"
                className="size-8"
                disabled={isFetching}
                onClick={() => refetch()}
              >
                <RefreshCw className={cn("size-4", isFetching && "animate-spin")} aria-hidden />
                <span className="sr-only">Refresh</span>
              </Button>
            </TooltipTrigger>
            <TooltipContent>Refresh</TooltipContent>
          </Tooltip>
        </div>
      </div>

      {/* ── Active filter chips ──────────────────────────────────────────── */}
      {filtersActive && (
        <div className="flex flex-wrap items-center gap-1.5 border-t border-border px-4 py-2">
          <span className="text-2xs uppercase tracking-wider text-muted-foreground">Filtered</span>
          {params.q && (
            <Chip onRemove={() => setParams({ q: "", page: 1 })}>
              search: <span className="font-semibold">{params.q}</span>
            </Chip>
          )}
          {params.status.map((s) => (
            <Chip
              key={s}
              onRemove={() => setParams({ status: params.status.filter((x) => x !== s), page: 1 })}
            >
              {s}
            </Chip>
          ))}
          <Button
            variant="ghost"
            size="sm"
            className="h-6 px-2 text-2xs text-muted-foreground"
            onClick={() => setParams({ status: [], q: "", page: 1 })}
          >
            Clear all
          </Button>
        </div>
      )}

      {/* ── Table ────────────────────────────────────────────────────────── */}
      <div className="scrollbar-thin relative overflow-x-auto border-t border-border">
        <table className="w-full border-separate border-spacing-0">
          <thead>
            <tr>
              {table.getHeaderGroups()[0]?.headers.map((header, i) => {
                const field = (header.column.columnDef.meta as any)?.field as
                  | RecordViewField
                  | undefined;
                const sortable = !!field;
                const sorted = params.sort === header.column.id;
                const dir = sorted ? params.dir : undefined;
                const numeric = !!(header.column.columnDef.meta as any)?.numeric;

                return (
                  <th
                    key={header.id}
                    scope="col"
                    // aria-sort belongs on the header cell, not the button inside.
                    aria-sort={
                      sorted
                        ? dir === "asc"
                          ? "ascending"
                          : "descending"
                        : sortable
                          ? "none"
                          : undefined
                    }
                    className={cn(
                      "sticky top-0 z-10 whitespace-nowrap border-b border-border bg-accent",
                      // Violet-on-violet keeps the header part of the theme
                      // rather than a grey band bolted onto it.
                      "text-xs font-semibold uppercase tracking-wider text-accent-foreground",
                      numeric ? "text-right" : "text-left",
                      i === 0 ? "pl-4 pr-3.5" : "px-3.5",
                    )}
                    style={{ width: header.getSize() === 150 ? undefined : header.getSize() }}
                  >
                    {sortable ? (
                      <button
                        type="button"
                        onClick={() =>
                          setParams({
                            sort: header.column.id,
                            dir: sorted && params.dir === "asc" ? "desc" : "asc",
                            page: 1,
                          })
                        }
                        className={cn(
                          "group/th inline-flex items-center gap-1 py-3 transition-colors hover:text-foreground",
                          sorted && "text-primary",
                        )}
                      >
                        {flexRender(header.column.columnDef.header, header.getContext())}
                        {sorted ? (
                          dir === "asc" ? (
                            <ArrowUp className="size-3" aria-hidden />
                          ) : (
                            <ArrowDown className="size-3" aria-hidden />
                          )
                        ) : (
                          <ArrowUpDown
                            className="size-3 opacity-0 transition-opacity group-hover/th:opacity-60"
                            aria-hidden
                          />
                        )}
                      </button>
                    ) : (
                      <div className="py-3">
                        {flexRender(header.column.columnDef.header, header.getContext())}
                      </div>
                    )}
                  </th>
                );
              })}
            </tr>
          </thead>

          <tbody className={cn(isPlaceholderData && "opacity-50 transition-opacity")}>
            {rows.length === 0 ? (
              <tr>
                <td colSpan={table.getVisibleLeafColumns().length} className="px-4">
                  <EmptyState
                    filtered={filtersActive}
                    onClear={() => setParams({ status: [], q: "", page: 1 })}
                  />
                </td>
              </tr>
            ) : (
              table.getRowModel().rows.map((row) => {
                const selected = row.getIsSelected();
                return (
                  <tr
                    key={row.id}
                    // Rows are focusable and Enter/Space activatable; previously
                    // the click handler sat on a non-focusable <tr>.
                    tabIndex={0}
                    role="link"
                    aria-label={`Open record ${row.id}`}
                    onClick={() => openRow(row.original)}
                    onKeyDown={(e) => {
                      if (e.key === "Enter" || e.key === " ") {
                        e.preventDefault();
                        openRow(row.original);
                      }
                    }}
                    className={cn(
                      "group/row cursor-pointer transition-colors",
                      selected ? "bg-accent/60" : "hover:bg-accent/40",
                    )}
                  >
                    {row.getVisibleCells().map((cell, ci) => (
                      <td
                        key={cell.id}
                        onClick={
                          // The action cell must not open the row.
                          cell.column.id === "__actions"
                            ? (e) => e.stopPropagation()
                            : undefined
                        }
                        className={cn(
                          "border-b border-border/60 text-base",
                          DENSITY_CELL[params.density as Density],
                          ci === 0 ? "pl-4 pr-3.5" : "px-3.5",
                          (cell.column.columnDef.meta as any)?.numeric && "text-right",
                          cell.column.id === "__actions" && "text-right",
                        )}
                      >
                        {flexRender(cell.column.columnDef.cell, cell.getContext())}
                      </td>
                    ))}
                  </tr>
                );
              })
            )}
          </tbody>

          {/* Totals for the rows actually on screen. Explicitly labelled "this
              page" so it can't be mistaken for a total across the filter. */}
          {rows.length > 0 && Object.keys(pageTotals).length > 0 && (
            <tfoot>
              <tr>
                {table.getVisibleLeafColumns().map((col, ci) => {
                  const total = pageTotals[col.id];
                  return (
                    <td
                      key={col.id}
                      className={cn(
                        "border-t border-border bg-muted/40 py-2.5",
                        ci === 0 ? "pl-4 pr-3.5" : "px-3.5",
                        total != null && "text-right",
                      )}
                    >
                      {total != null ? (
                        <span className="text-base font-bold tabular-nums text-primary">
                          {formatCurrency(total)}
                        </span>
                      ) : ci === 0 ? (
                        <span className="text-2xs font-semibold uppercase tracking-wider text-muted-foreground">
                          Page total · {rows.length} rows
                        </span>
                      ) : null}
                    </td>
                  );
                })}
              </tr>
            </tfoot>
          )}
        </table>
      </div>

      {/* ── Pagination ───────────────────────────────────────────────────── */}
      {total_count > 0 && (
        <div className="flex flex-wrap items-center justify-between gap-3 px-4 py-2.5">
          <div className="flex items-center gap-3 text-sm text-muted-foreground">
            <div className="flex items-center gap-1.5">
              <span>Rows</span>
              <Select
                value={String(params.limit)}
                onValueChange={(v) => setParams({ limit: Number(v), page: 1 })}
              >
                <SelectTrigger size="sm" className="h-7 w-[68px]" aria-label="Rows per page">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {[10, 25, 50, 100].map((n) => (
                    <SelectItem key={n} value={String(n)}>
                      {n}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <Separator orientation="vertical" className="h-4" />
            <span className="tabular-nums">
              {from}–{to} of {total_count}
            </span>
          </div>

          {total_pages > 1 && (
            <nav aria-label="Pagination" className="flex items-center gap-0.5">
              <Button
                variant="ghost"
                size="icon"
                className="size-7"
                disabled={page <= 1}
                onClick={() => setParams({ page: page - 1 })}
              >
                <ChevronLeft className="size-4" aria-hidden />
                <span className="sr-only">Previous page</span>
              </Button>

              {buildPageRange(page, total_pages).map((p, idx) =>
                p === "…" ? (
                  <span
                    key={`gap-${idx}`}
                    className="flex size-7 select-none items-center justify-center text-sm text-muted-foreground"
                  >
                    …
                  </span>
                ) : (
                  <Button
                    key={p}
                    variant={p === page ? "default" : "ghost"}
                    size="icon"
                    className="size-7 text-sm tabular-nums"
                    aria-current={p === page ? "page" : undefined}
                    onClick={() => setParams({ page: p as number })}
                  >
                    {p}
                  </Button>
                ),
              )}

              <Button
                variant="ghost"
                size="icon"
                className="size-7"
                disabled={page >= total_pages}
                onClick={() => setParams({ page: page + 1 })}
              >
                <ChevronRight className="size-4" aria-hidden />
                <span className="sr-only">Next page</span>
              </Button>
            </nav>
          )}
        </div>
      )}

      {formModal && (
        <FormModal
          activityId={formModal.activityId}
          instanceId={formModal.instanceId}
          title={formModal.title}
          onClose={() => setFormModal(null)}
          onSuccess={afterAction}
        />
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Toolbar pieces
// ---------------------------------------------------------------------------

function Chip({ children, onRemove }: { children: React.ReactNode; onRemove: () => void }) {
  return (
    <span className="inline-flex items-center gap-1 rounded-md border border-primary/25 bg-primary/10 px-2 py-0.5 text-xs text-primary">
      {children}
      <button onClick={onRemove} className="rounded hover:opacity-60">
        <X className="size-2.5" aria-hidden />
        <span className="sr-only">Remove filter</span>
      </button>
    </span>
  );
}

// ---------------------------------------------------------------------------
// Row actions
// ---------------------------------------------------------------------------

function RowActions({
  row,
  onAction,
  onOpen,
}: {
  row: Row;
  onAction: (a: StateAction) => void;
  onOpen: () => void;
}) {
  const actions = actionsForStateName(String(row[STATE_FIELD] ?? ""));

  return (
    <div className="flex items-center justify-end gap-0.5 opacity-0 transition-opacity focus-within:opacity-100 group-hover/row:opacity-100">
      {actions.length > 0 && (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="ghost" size="icon" className="size-7">
              <MoreHorizontal className="size-4" aria-hidden />
              <span className="sr-only">Actions for this record</span>
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end" className="w-44">
            {actions.map((a) => (
              <DropdownMenuItem
                key={a.activityId}
                variant={a.destructive ? "destructive" : "default"}
                onClick={() => onAction(a)}
              >
                <a.icon className="size-3.5" aria-hidden />
                {a.label}
              </DropdownMenuItem>
            ))}
          </DropdownMenuContent>
        </DropdownMenu>
      )}
      <Button variant="ghost" size="icon" className="size-7" onClick={onOpen}>
        <ChevronRight className="size-4" aria-hidden />
        <span className="sr-only">Open record</span>
      </Button>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Cells / empty state
// ---------------------------------------------------------------------------

function renderCell(value: unknown, field: RecordViewField, isPrimary: boolean) {
  const { field_key: key, data_type: type } = field;

  if (key === STATE_FIELD) return <StatusBadge value={String(value ?? "")} />;

  if (value == null || value === "") {
    return <span className="text-muted-foreground/40">—</span>;
  }

  if (VENDOR_KEYS.includes(key)) return <VendorCell name={String(value)} />;

  if (type === "number") {
    const n = Number(value);
    if (!Number.isFinite(n)) return String(value);
    return isMoneyField(key) ? (
      <span className="font-medium tabular-nums text-foreground">{formatCurrency(n)}</span>
    ) : (
      <span className="tabular-nums">{n.toLocaleString("en-IN")}</span>
    );
  }

  if (type === "date" || type === "datetime") {
    const time = formatTime(String(value));
    return (
      <span className="block whitespace-nowrap leading-tight">
        {formatDate(String(value))}
        {time && <span className="block text-2xs text-muted-foreground">{time}</span>}
      </span>
    );
  }

  return (
    <span className={cn("block max-w-[28ch] truncate", isPrimary && "font-semibold text-foreground")}>
      {String(value)}
    </span>
  );
}

/** Company name with an initials avatar — gives every row a visual anchor. */
function VendorCell({ name }: { name: string }) {
  const initials = name
    .split(/\s+/)
    .filter((w) => /[a-z0-9]/i.test(w))
    .slice(0, 2)
    .map((w) => w[0]!.toUpperCase())
    .join("");
  const tint = colorForName(name);

  return (
    <span className="flex items-center gap-2.5">
      <span
        aria-hidden
        className="flex size-9 shrink-0 items-center justify-center rounded-full text-xs font-bold"
        style={{ background: `color-mix(in oklab, ${tint} 15%, transparent)`, color: tint }}
      >
        {initials || "?"}
      </span>
      <span className="block max-w-[22ch] truncate font-medium text-foreground">{name}</span>
    </span>
  );
}

function EmptyState({ filtered, onClear }: { filtered: boolean; onClear: () => void }) {
  return (
    <div className="flex flex-col items-center gap-3 py-16 text-center">
      <div className="flex size-12 items-center justify-center rounded-xl bg-accent">
        <Inbox className="size-5 text-primary" aria-hidden />
      </div>
      <div>
        <p className="text-md font-semibold text-foreground">No records found</p>
        <p className="mt-0.5 text-sm text-muted-foreground">
          {filtered
            ? "No invoices match the current filters."
            : "Submit a new invoice to get started."}
        </p>
      </div>
      {filtered && (
        <Button variant="outline" size="sm" onClick={onClear}>
          Clear filters
        </Button>
      )}
    </div>
  );
}

function buildPageRange(current: number, total: number): Array<number | "…"> {
  if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
  if (current <= 4) return [1, 2, 3, 4, 5, "…", total];
  if (current >= total - 3) return [1, "…", total - 4, total - 3, total - 2, total - 1, total];
  return [1, "…", current - 1, current, current + 1, "…", total];
}
