/**
 * @license
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * Orders page — replicated from the operations console (nearle_console/orders),
 * rebuilt in the merchant stack against the shared console UI kit (`./consoleUi`)
 * so it matches the source design: brand purple #662582, gradient header, KPI
 * cards with gradient top-bars, pill status tabs, and a status-chip table. Wired
 * to the live Fiesta order endpoints (status-scoped, date-ranged, paginated).
 */

import React, { useMemo, useState, useRef, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { ShoppingBag, Clock, CheckCircle2, XCircle, Calendar, ChevronLeft, ChevronRight, Package, MapPin, Phone, X, Loader2, Download, UserCheck, ClipboardList, ArrowLeft } from 'lucide-react';
import { useFiestaOrderSummary, useFiestaOrders, useFiestaOrderDetails, useFiestaRiders, useFiestaAssignRider, useFiestaNotifyRider } from '../services/fiestaQueries';
import {
  FIESTA_TENANT_ID, FIESTA_APPLOCATION_ID, RIDER_MESSAGES, RiderNotReachableError,
  num as fnum, str as fstr, ymd, type Row,
} from '../services/fiestaApi';
import { shortTime } from '../services/fiestaMappers';
import {
  GradientHeader, LiveStatus, KpiStrip, Pill, StatusChip, MetricPill, SearchPill, FilterBar, TH_STYLE,
  ORDER_STATUS, statusColor, BRAND, BRAND_LIGHT, TEXT, TEXT_2, TEXT_3, BORDER, SURFACE_ALT, tint, soft, edge, ring,
} from './consoleUi';

interface OrdersViewProps {
  searchQuery?: string;
  locationid?: number;
  /** Merchant tenant to scope to; defaults to the shared constant. */
  tenantId?: number;
  /**
   * App-location to source assignable riders from. Riders are scoped by
   * app-location rather than tenant, so without one the rider list falls back
   * to whatever the visible orders name. Defaults to the platform constant.
   */
  applocationid?: number;
  date?: string;
}

type StatusKey = 'created' | 'pending' | 'processing' | 'delivered' | 'cancelled';
const STATUS_TABS: Array<{ key: StatusKey; label: string }> = [
  { key: 'created', label: 'Created' },
  { key: 'pending', label: 'Pending' },
  { key: 'processing', label: 'Processing' },
  { key: 'delivered', label: 'Delivered' },
  { key: 'cancelled', label: 'Cancelled' },
];
const PAGE_SIZE = 25;

export default function OrdersView({
  searchQuery = '',
  locationid,
  tenantId = FIESTA_TENANT_ID,
  applocationid = FIESTA_APPLOCATION_ID,
  date,
}: OrdersViewProps) {
  const today = new Date();
  const monthStart = new Date(today.getFullYear(), today.getMonth(), 1);
  const [fromdate, setFromdate] = useState<string>(date || ymd(today));
  const [todate, setTodate] = useState<string>(date || ymd(today));

  // Sync internal date range if the prop changes from the Hub header
  useEffect(() => {
    if (date) {
      setFromdate(date);
      setTodate(date);
    }
  }, [date]);

  const dayOffset = (n: number) => { const d = new Date(); d.setDate(d.getDate() - n); return ymd(d); };
  const dayAhead = (n: number) => { const d = new Date(); d.setDate(d.getDate() + n); return ymd(d); };
  // NOTE: the backend lists orders by DELIVERY date (deliverytime), not creation
  // date — so an order created today for a future slot only appears once the range
  // covers its delivery date. "Next 7 Days" surfaces upcoming-delivery orders.
  // "All time" can't pass empty dates (the query is gated on from/to), so it uses
  // a wide window — from the platform's earliest plausible data to a year ahead.
  const presets = [
    { key: 'today', label: 'Today',       from: ymd(today),      to: ymd(today)  },
    { key: 'yesterday', label: 'Yesterday', from: dayOffset(1),  to: dayOffset(1) },
    { key: '7d',    label: 'Last 7 Days', from: dayOffset(6),    to: ymd(today)  },
    { key: 'month', label: 'This Month',  from: ymd(monthStart), to: dayAhead(7) },
  ];
  const activePreset = presets.find((p) => p.from === fromdate && p.to === todate)?.key ?? 'custom';

  const [status, setStatus] = useState<StatusKey>('created');
  const [pageno, setPageno] = useState(1);
  const [localSearch, setLocalSearch] = useState('');
  const [branch, setBranch] = useState(0); // applocationid filter (0 = all branches)
  const [detailOrder, setDetailOrder] = useState<Row | null>(null);

  // ── Multi-select rider assignment (parity with the ops console) ─────────────
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [assignRiderId, setAssignRiderId] = useState(0);
  const [assignMsg, setAssignMsg] = useState('');
  const [showSelected, setShowSelected] = useState(false); // full-page review of selection
  const assignMut = useFiestaAssignRider();
  const notifyMut = useFiestaNotifyRider();

  // Ctrl/Cmd+K focuses search; Escape blurs it (parity with the ops console).
  const searchRef = useRef<HTMLInputElement>(null);
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        searchRef.current?.focus();
      } else if (e.key === 'Escape' && document.activeElement === searchRef.current) {
        searchRef.current?.blur();
      }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, []);

  // Reset the selection whenever the visible result set changes, so an assign
  // can never act on rows the operator can no longer see.
  useEffect(() => {
    setSelected(new Set());
    setAssignMsg('');
    setShowSelected(false);
  }, [fromdate, todate, status, branch, pageno, locationid]);

  // 'all' lists every rider on duty at this app-location; 'partner' narrows to
  // the partner already carrying the selected orders. Defaults to 'all' — the
  // old default filtered to riders with no partnerid, which no on-duty rider has.
  const [riderSource, setRiderSource] = useState<'all' | 'partner'>('all');

  // Scope to the user's store when a locationid is supplied (server-side per the
  // backend's getordersummary/getorders locationid param); tenant-wide otherwise.
  const summaryQ = useFiestaOrderSummary(tenantId, fromdate, todate, locationid);
  const ordersQ = useFiestaOrders({ tenantid: tenantId, status, fromdate, todate, locationid, pageno, pagesize: PAGE_SIZE });
  const summary = summaryQ.data;
  const rawRows = ordersQ.data ?? [];

  // Riders are scoped by app-location, NOT by tenant. A rider record carries a
  // partnerid and an applocationid but leaves app_users.tenantid unset, so
  // /partners/getriders?tenantid=… returns an empty list for every tenant —
  // which is what the assign dropdown used to show. The app-location is taken
  // from the live order rows, falling back to the signed-in user's own.
  const orderPartnerId = useMemo(() => fnum(rawRows.find((r) => fnum(r.partnerid))?.partnerid), [rawRows]);
  const orderApplocationId = useMemo(() => fnum(rawRows.find((r) => fnum(r.applocationid))?.applocationid), [rawRows]);
  const riderApplocationId = orderApplocationId || applocationid || 0;

  // /partners/getriders is already a live-presence query, not a roster: the
  // backend filters on status='Active', onduty=1 and a riderlog dated today
  // with logstatus=0, joined to each rider's most recent GPS ping. So this
  // returns riders who are on shift and logged in right now, and it carries the
  // userfcmtoken needed to notify them.
  const ridersQ = useFiestaRiders({
    applocationid: riderApplocationId || undefined,
  });

  // The previous build also merged in getallusers?roleid=5 as an "own fleet".
  // There is no rider role: app_roles only defines 1-6 as Super admin /
  // Operations / Admin / Manager per configid, and riders are identified by
  // configid=6 inside getriders. roleid=5 matched a single user with two
  // deliveries in the platform's history, while the 29 users who actually drive
  // the bulk of deliveries carry roleid 0. Every on-duty rider also has
  // partnerid > 0, so the "own fleet" tab — the default — filtered that list
  // down to nothing and the dropdown was empty on open.
  const riderOptions = useMemo(
    () =>
      (ridersQ.data ?? [])
        .filter((r) => {
          if (riderSource === 'all') return true;
          // Restrict to the partner already carrying these orders, when the
          // rows name one; otherwise there is nothing to narrow to.
          const pId = fnum(r.partnerid);
          return !orderPartnerId || pId === orderPartnerId;
        })
        .map((r) => ({
          id: fnum(r.userid),
          label:
            (fstr(r.fullname) || `${fstr(r.firstname)} ${fstr(r.lastname)}`).trim() +
            (fstr(r.contactno) ? ` · ${fstr(r.contactno)}` : ''),
          // Carried so the assign can notify the rider, and so a rider with no
          // registered device can be called out rather than silently skipped.
          token: fstr(r.userfcmtoken),
          vehicle: fstr(r.vehiclename),
        }))
        .filter((o) => o.id > 0 && o.label),
    [ridersQ.data, riderSource, orderPartnerId],
  );

  // Branches (app-locations) present in the data — drives the branch filter so the
  // operator can see which branch an order was placed at. Each order row carries
  // applocationid + applocation (the app-location name).
  const branches = useMemo(() => {
    const m = new Map<number, string>();
    for (const r of rawRows) {
      const id = fnum(r.applocationid);
      if (id && !m.has(id)) m.set(id, fstr(r.applocation) || fstr(r.locationname) || `Branch ${id}`);
    }
    return [...m.entries()].map(([id, name]) => ({ id, name }));
  }, [rawRows]);

  const rows = useMemo(() => {
    const term = (localSearch || searchQuery).toLowerCase();
    return rawRows.filter((r) => {
      if (locationid && fnum(r.locationid) !== locationid) return false;
      if (branch && fnum(r.applocationid) !== branch) return false;
      if (!term) return true;
      // Broad match across every order field shown or relevant (mirrors the ops
      // console search): id, both parties + contacts + addresses, branch, rider,
      // status, and notes.
      return [
        r.orderid, r.orderstatus, r.ordernotes, r.tenantname,
        r.pickupcustomer, r.pickupcontactno, r.pickupsuburb, r.pickupaddress, r.pickuplocation,
        r.deliverycustomer, r.deliverycontactno, r.deliverysuburb, r.deliveryaddress, r.deliverylocation,
        r.applocation, r.locationname, r.ridername,
      ].some((v) => fstr(v).toLowerCase().includes(term));
    });
  }, [rawRows, localSearch, searchQuery, locationid, branch]);

  // Footer totals across the filtered rows (parity with the ops console's
  // Total Charges / Total Amount summary).
  const totals = useMemo(() => {
    let cod = 0, charges = 0, amount = 0;
    for (const r of rows) {
      cod += fnum(r.collectionamt);
      charges += fnum(r.deliverycharge) || fnum(r.deliverycharges);
      amount += fnum(r.orderamount) || fnum(r.deliveryamt);
    }
    return { cod, charges, amount };
  }, [rows]);

  const inr = (n: number) => `₹${n.toLocaleString('en-IN')}`;

  // Export the currently-filtered orders to CSV (RFC-4180 quoting).
  const exportCsv = () => {
    const headers = ['#', 'Order ID', 'Status', 'Branch', 'Order Date', 'Pickup', 'Pickup Contact', 'Pickup Address', 'Drop', 'Drop Contact', 'Drop Address', 'Qty', 'COD', 'KMs', 'Charges', 'Amount'];
    const esc = (v: unknown) => `"${fstr(v).replace(/"/g, '""')}"`;
    const lines = rows.map((r, i) => [
      i + 1, fstr(r.orderid) || fstr(r.orderheaderid), fstr(r.orderstatus), fstr(r.applocation) || fstr(r.locationname),
      shortTime(r.orderdate || r.deliverydate), fstr(r.pickupcustomer) || fstr(r.tenantname), fstr(r.pickupcontactno),
      fstr(r.pickupaddress) || fstr(r.pickupsuburb), fstr(r.deliverycustomer), fstr(r.deliverycontactno),
      fstr(r.deliveryaddress) || fstr(r.deliverysuburb), fnum(r.quantity), fnum(r.collectionamt),
      fnum(r.kms), fnum(r.deliverycharge) || fnum(r.deliverycharges), fnum(r.orderamount) || fnum(r.deliveryamt),
    ].map(esc).join(','));
    const blob = new Blob([[headers.join(','), ...lines].join('\n')], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `Orders_${status}_${fromdate}_to_${todate}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const hasNext = rawRows.length === PAGE_SIZE;
  const total = summary?.total ?? 0;
  const pct = (n: number) => (total > 0 ? Math.round((n / total) * 100) : 0);
  const countFor = (key: StatusKey): number => (summary ? (summary[key] ?? 0) : 0);

  // Restrained, professional palette — deep muted tones (not neon) so the KPI
  // strip reads as a serious business dashboard rather than a colourful one.
  const kpis = [
    { label: 'Created Orders', value: (summary?.created ?? 0).toLocaleString('en-IN'), color: '#6366f1', icon: <ShoppingBag size={20} />, badge: `${pct(summary?.created ?? 0)}% of total` },
    { label: 'Pending Orders', value: (summary?.pending ?? 0).toLocaleString('en-IN'), color: '#f59e0b', icon: <Clock size={20} />, badge: `${pct(summary?.pending ?? 0)}% of total` },
    { label: 'Delivered Orders', value: (summary?.delivered ?? 0).toLocaleString('en-IN'), color: '#10b981', icon: <CheckCircle2 size={20} />, badge: `${pct(summary?.delivered ?? 0)}% of total` },
    { label: 'Cancelled Orders', value: (summary?.cancelled ?? 0).toLocaleString('en-IN'), color: '#f43f5e', icon: <XCircle size={20} />, badge: `${pct(summary?.cancelled ?? 0)}% of total` },
  ];

  const setScope = (next: Partial<{ status: StatusKey; from: string; to: string }>) => {
    if (next.status) setStatus(next.status);
    if (next.from) setFromdate(next.from);
    if (next.to) setTodate(next.to);
    setPageno(1);
  };

  // ── Selection helpers ───────────────────────────────────────────────────────
  const rowKey = (r: Row) => fstr(r.orderheaderid) || fstr(r.orderid);
  const assignableRows = rows.filter((r) => fstr(r.orderstatus).toLowerCase() === 'created');
  const assignableKeys = assignableRows.map(rowKey);
  const allSelected = assignableKeys.length > 0 && assignableKeys.every((k) => selected.has(k));

  const toggleRow = (k: string) =>
    setSelected((prev) => {
      const n = new Set(prev);
      if (n.has(k)) n.delete(k);
      else n.add(k);
      return n;
    });

  const toggleAll = () =>
    setSelected((prev) => {
      const n = new Set(prev);
      if (allSelected) assignableKeys.forEach((k) => n.delete(k));
      else assignableKeys.forEach((k) => n.add(k));
      return n;
    });

  const handleAssign = async () => {
    if (!assignRiderId || selected.size === 0) return;
    const toAssign = rows.filter((r) => selected.has(rowKey(r)));
    const option = riderOptions.find((o) => o.id === assignRiderId);
    const rider = option?.label ?? 'rider';
    try {
      const res = await assignMut.mutateAsync({ userid: assignRiderId, orders: toAssign });
      const assigned =
        res.failed
          ? `Assigned ${res.ok}/${res.total} to ${rider} · ${res.failed} failed`
          : `Assigned ${res.ok} order${res.ok === 1 ? '' : 's'} to ${rider}`;
      setSelected(new Set());
      setShowSelected(false); // return to the board with the result shown in the bar

      // Notify only for work that actually landed. The push runs after the
      // write and is reported separately: the deliveries exist either way, so a
      // failed notification must not read as a failed assignment — but it must
      // still be visible, because a rider who was never told has work sitting
      // unseen.
      if (res.ok === 0) {
        setAssignMsg(assigned);
        return;
      }
      setAssignMsg(`${assigned} · notifying…`);
      try {
        await notifyMut.mutateAsync({
          token: option?.token ?? '',
          body: RIDER_MESSAGES.assigned(res.ok),
        });
        setAssignMsg(`${assigned} · rider notified`);
      } catch (err) {
        setAssignMsg(
          `${assigned} · NOT notified — ${
            err instanceof RiderNotReachableError
              ? 'this rider has no device registered'
              : 'the push failed, tell them another way'
          }`,
        );
      }
    } catch {
      setAssignMsg('Assignment failed — please retry.');
    }
  };

  // Rows currently selected (selection is always within the visible page).
  const selectedRows = useMemo(() => rows.filter((r) => selected.has(rowKey(r))), [rows, selected]);

  return (
    <div className="animate-in fade-in duration-300">
      <div className="mb-4">

        <KpiStrip items={kpis} loading={summaryQ.isLoading} />
      </div>

      {/* Date filter */}
      <FilterBar className="mb-4">
        <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-3">
          <div className="flex items-center gap-2 flex-wrap">
            <span className="inline-flex items-center gap-1.5 text-[10px] font-extrabold uppercase tracking-widest pr-1" style={{ color: TEXT_2 }}>
              <Calendar size={13} style={{ color: BRAND }} /> View
            </span>
            {presets.map((p) => (
              <React.Fragment key={p.key}>
                <Pill active={activePreset === p.key} color={BRAND} onClick={() => setScope({ from: p.from, to: p.to })}>{p.label}</Pill>
              </React.Fragment>
            ))}
          </div>
          <div className="flex items-center gap-2 text-xs">
            <input type="date" value={fromdate} max={todate} onChange={(e) => setScope({ from: e.target.value })}
              className="rounded-full outline-none font-semibold" style={{ padding: '6px 12px', border: `1px solid ${BORDER}`, background: '#fff', color: TEXT_2 }} />
            <span style={{ color: TEXT_3 }}>→</span>
            <input type="date" value={todate} min={fromdate} onChange={(e) => setScope({ to: e.target.value })}
              className="rounded-full outline-none font-semibold" style={{ padding: '6px 12px', border: `1px solid ${BORDER}`, background: '#fff', color: TEXT_2 }} />
          </div>
        </div>
      </FilterBar>

      {/* Status tabs + search */}
      <FilterBar className="mb-4">
        <div className="flex flex-col lg:flex-row lg:items-center gap-3">
          <div className="flex items-center gap-2 overflow-x-auto custom-scrollbar no-scrollbar py-0.5 flex-1 min-w-0 touch-scrolling">
            {STATUS_TABS.map((t) => {
              // Single brand accent for the tab row (calmer than per-status colours);
              // the per-status hue still appears on the row Status chip where it aids scanning.
              const color = BRAND;
              return (
                <React.Fragment key={t.key}>
                  <Pill active={status === t.key} color={color} onClick={() => setScope({ status: t.key })} count={summaryQ.isLoading ? '·' : countFor(t.key).toLocaleString('en-IN')}>
                    {t.label}
                  </Pill>
                </React.Fragment>
              );
            })}
          </div>
          <div className="flex items-center gap-2 w-full lg:w-auto lg:shrink-0">
            {branches.length > 1 && (
              <select
                value={branch}
                onChange={(e) => { setBranch(Number(e.target.value)); setPageno(1); }}
                title="Filter by branch / app-location"
                className="rounded-full font-bold text-xs outline-none cursor-pointer shrink-0"
                style={{ padding: '7px 12px', border: `1.5px solid ${edge(BRAND)}`, background: tint(BRAND), color: BRAND }}
              >
                <option value={0}>All branches</option>
                {branches.map((b) => <option key={b.id} value={b.id}>{b.name}</option>)}
              </select>
            )}
            <div className="w-full lg:w-60"><SearchPill value={localSearch} onChange={setLocalSearch} placeholder="Search orders (Ctrl+K)…" inputRef={searchRef} /></div>
            <button
              onClick={exportCsv}
              disabled={rows.length === 0}
              title="Export current view to CSV"
              className="inline-flex items-center gap-1.5 rounded-full font-extrabold text-white cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed whitespace-nowrap shrink-0"
              style={{ padding: '8px 14px', fontSize: 12, background: `linear-gradient(135deg, ${BRAND}, ${BRAND_LIGHT})`, boxShadow: `0 6px 18px ${ring(BRAND)}` }}
            >
              <Download size={13} /> CSV
            </button>
          </div>
        </div>
      </FilterBar>

      {/* Multi-select assign bar — shown while rows are selected (or to report a result) */}
      {(selected.size > 0 || assignMsg) && (
        <div className="mb-3 flex flex-wrap items-center gap-2 rounded-2xl px-4 py-3 animate-in fade-in slide-in-from-top-1 duration-200" style={{ background: tint(BRAND), border: `1.5px solid ${edge(BRAND)}` }}>
          <span className="inline-flex items-center gap-1.5 font-extrabold text-xs" style={{ color: BRAND }}>
            <UserCheck size={15} /> {selected.size} selected
          </span>
          <div className="flex bg-white rounded-full p-0.5 ml-2 border" style={{ borderColor: edge(BRAND) }}>
            <button
              onClick={() => setRiderSource('all')}
              title="Every rider on duty at this branch right now"
              className={`px-3 py-1 text-[11px] font-bold rounded-full transition-colors ${riderSource === 'all' ? '' : 'text-slate-500 hover:bg-slate-50'}`}
              style={riderSource === 'all' ? { background: tint(BRAND), color: BRAND } : undefined}
            >
              On Duty
            </button>
            <button
              onClick={() => setRiderSource('partner')}
              disabled={!orderPartnerId}
              title={orderPartnerId ? 'Only riders from the partner carrying these orders' : 'These orders name no partner'}
              className={`px-3 py-1 text-[11px] font-bold rounded-full transition-colors disabled:opacity-40 ${riderSource === 'partner' ? '' : 'text-slate-500 hover:bg-slate-50'}`}
              style={riderSource === 'partner' ? { background: tint(BRAND), color: BRAND } : undefined}
            >
              This Partner
            </button>
          </div>
          <select
            value={assignRiderId}
            onChange={(e) => setAssignRiderId(Number(e.target.value))}
            disabled={selected.size === 0}
            title="Choose a rider to assign"
            className="rounded-full font-bold text-xs outline-none cursor-pointer disabled:opacity-50"
            style={{ padding: '7px 12px', border: `1.5px solid ${edge(BRAND)}`, background: '#fff', color: BRAND, maxWidth: 260 }}
          >
            <option value={0}>
              {ridersQ.isLoading
                ? 'Loading riders…'
                : riderOptions.length
                  ? 'Select rider…'
                  : riderSource === 'partner'
                    ? 'No riders on duty for this partner'
                    : 'No riders on duty right now'}
            </option>
            {riderOptions.map((o) => <option key={o.id} value={o.id}>{o.label}</option>)}
          </select>
          <button
            onClick={handleAssign}
            disabled={!assignRiderId || selected.size === 0 || assignMut.isPending}
            className="inline-flex items-center gap-1.5 rounded-full font-extrabold text-white cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
            style={{ padding: '7px 14px', fontSize: 12, background: `linear-gradient(135deg, ${BRAND}, ${BRAND_LIGHT})`, boxShadow: `0 6px 18px ${ring(BRAND)}` }}
          >
            {assignMut.isPending ? <Loader2 size={13} className="animate-spin" /> : <UserCheck size={13} />} Assign rider
          </button>
          {selected.size > 0 && (
            <button onClick={() => setSelected(new Set())} className="rounded-full font-bold text-xs cursor-pointer" style={{ padding: '7px 12px', border: `1px solid ${BORDER}`, background: '#fff', color: TEXT_2 }}>
              Clear
            </button>
          )}
          {assignMsg && <span className="text-[11px] font-semibold ml-auto" style={{ color: TEXT_2 }}>{assignMsg}</span>}
        </div>
      )}

      {/* Table */}
      <div className="bg-white border overflow-hidden" style={{ borderColor: BORDER }}>
        <div className="overflow-x-auto">
          <table className="w-full" style={{ minWidth: 960 }}>
            <thead>
              <tr>
                <th className="px-3 py-2.5 text-left" style={TH_STYLE}>
                  <input type="checkbox" checked={allSelected} onChange={toggleAll} disabled={assignableRows.length === 0} aria-label="Select all assignable orders" style={{ accentColor: BRAND, cursor: 'pointer', width: 15, height: 15 }} />
                </th>
                {['#', 'Order', 'Branch', 'Pickup', 'Drop', 'Qty', 'COD', 'KMs', 'Charges', 'Status', ''].map((h, i) => (
                  <th key={i} className="px-3 py-2.5 text-left" style={TH_STYLE}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {ordersQ.isLoading ? (
                <tr><td colSpan={12} className="px-3 py-12 text-center" style={{ color: TEXT_3 }}>
                  <span className="inline-flex items-center gap-2 text-xs font-semibold"><Loader2 size={15} className="animate-spin" style={{ color: BRAND }} /> Loading orders…</span>
                </td></tr>
              ) : rows.length === 0 ? (
                <tr><td colSpan={12} className="px-3 py-12 text-center text-xs" style={{ color: TEXT_3 }}>No orders found for this status, date range, or search.</td></tr>
              ) : (
                rows.map((r, i) => {
                  const st = fstr(r.orderstatus).toLowerCase();
                  const cod = fnum(r.collectionamt);
                  const charges = fnum(r.deliverycharge) || fnum(r.deliverycharges);
                  return (
                    <tr key={fstr(r.orderid) || i} className="transition-colors" style={{ borderBottom: `1px solid ${DIVIDER_C}`, background: selected.has(rowKey(r)) ? tint(BRAND) : 'transparent' }}
                      onMouseEnter={(e) => { if (!selected.has(rowKey(r))) e.currentTarget.style.background = SURFACE_ALT; }} onMouseLeave={(e) => { e.currentTarget.style.background = selected.has(rowKey(r)) ? tint(BRAND) : 'transparent'; }}>
                      <td className="px-3 py-2.5">
                        {st === 'created' ? (
                          <input type="checkbox" checked={selected.has(rowKey(r))} onChange={() => toggleRow(rowKey(r))} aria-label="Select order" style={{ accentColor: BRAND, cursor: 'pointer', width: 15, height: 15 }} />
                        ) : (
                          <input type="checkbox" disabled aria-label="Order cannot be assigned" style={{ width: 15, height: 15, opacity: 0.3 }} title="Only 'created' orders can be assigned" />
                        )}
                      </td>
                      <td className="px-3 py-2.5 font-mono" style={{ color: TEXT_3 }}>{(pageno - 1) * PAGE_SIZE + i + 1}</td>
                      <td className="px-3 py-2.5">
                        <p className="font-extrabold font-mono text-[13px]" style={{ color: TEXT }}>{fstr(r.orderid) || `#${fstr(r.orderheaderid)}`}</p>
                        <p className="text-[10px]" style={{ color: TEXT_2 }}>{shortTime(r.orderdate || r.deliverydate)}</p>
                      </td>
                      <td className="px-3 py-2.5">
                        <span className="inline-flex items-center gap-1 font-bold text-[12px]" style={{ color: BRAND }}>
                          <MapPin size={11} /> {fstr(r.applocation) || '—'}
                        </span>
                        {fstr(r.locationname) && <p className="text-[10px] truncate max-w-[130px]" style={{ color: TEXT_2 }}>{fstr(r.locationname)}</p>}
                      </td>
                      <td className="px-3 py-2.5">
                        <p className="font-bold text-[12px] truncate max-w-[150px]" style={{ color: TEXT }}>{fstr(r.pickupcustomer) || fstr(r.tenantname) || '—'}</p>
                        <p className="text-[10px] truncate max-w-[150px]" style={{ color: TEXT_2 }}>{fstr(r.pickupsuburb) || fstr(r.pickupaddress)}</p>
                      </td>
                      <td className="px-3 py-2.5">
                        <p className="font-bold text-[12px] truncate max-w-[150px]" style={{ color: TEXT }}>{fstr(r.deliverycustomer) || '—'}</p>
                        <p className="text-[10px] truncate max-w-[150px]" style={{ color: TEXT_2 }}>{fstr(r.deliverysuburb) || fstr(r.deliveryaddress)}</p>
                      </td>
                      <td className="px-3 py-2.5 font-mono text-[12px]" style={{ color: TEXT }}>{fnum(r.quantity) || '—'}</td>
                      <td className="px-3 py-2.5 font-mono text-[12px] font-semibold" style={{ color: cod > 0 ? TEXT : TEXT_3 }}>{cod > 0 ? `₹${cod.toLocaleString('en-IN')}` : '—'}</td>
                      <td className="px-3 py-2.5 font-mono text-[12px]" style={{ color: fnum(r.kms) ? TEXT_2 : TEXT_3 }}>{fnum(r.kms) ? fnum(r.kms).toFixed(1) : '—'}</td>
                      <td className="px-3 py-2.5 font-mono text-[12px] font-semibold" style={{ color: charges > 0 ? TEXT : TEXT_3 }}>{charges > 0 ? `₹${charges.toLocaleString('en-IN')}` : '—'}</td>
                      <td className="px-3 py-2.5"><StatusChip label={st || '—'} color={statusColor(ORDER_STATUS, st)} /></td>
                      <td className="px-3 py-2.5 text-right">
                        <button onClick={() => setDetailOrder(r)} className="rounded-full font-extrabold cursor-pointer transition-colors"
                          style={{ padding: '4px 12px', fontSize: 11, color: BRAND, background: tint(BRAND), border: `1px solid ${edge(BRAND)}` }}>View</button>
                      </td>
                    </tr>
                  );
                })
              )}
            </tbody>
          </table>
        </div>
        {/* Totals across the filtered rows */}
        {rows.length > 0 && (
          <div className="flex flex-wrap items-center justify-end gap-2 px-4 py-2.5 border-t" style={{ borderColor: BORDER, background: SURFACE_ALT }}>
            <span className="text-[10px] font-extrabold uppercase tracking-wider mr-auto" style={{ color: TEXT_2 }}>Totals · {rows.length} order{rows.length === 1 ? '' : 's'}</span>
            {totals.cod > 0 && <TotalChip label="COD" value={inr(totals.cod)} color={TEXT_2} />}
            <TotalChip label="Charges" value={inr(totals.charges)} color={TEXT_2} />
            <TotalChip label="Amount" value={inr(totals.amount)} color={BRAND} />
          </div>
        )}
        <div className="flex items-center justify-between px-4 py-3 border-t" style={{ borderColor: BORDER, background: SURFACE_ALT }}>
          <span className="text-[10px] font-bold uppercase tracking-wider" style={{ color: TEXT_2 }}>Page {pageno} · {rows.length} shown</span>
          <div className="flex items-center gap-2">
            <PagerBtn disabled={pageno === 1} onClick={() => setPageno((p) => Math.max(1, p - 1))}><ChevronLeft size={13} /> Prev</PagerBtn>
            <PagerBtn disabled={!hasNext} onClick={() => setPageno((p) => p + 1)}>Next <ChevronRight size={13} /></PagerBtn>
          </div>
        </div>
      </div>

      {detailOrder && <OrderDetailModal order={detailOrder} onClose={() => setDetailOrder(null)} />}

      {/* Right-edge floating badge — only on the Created tab and only when
          MULTIPLE orders are selected (created orders are what get dispatched).
          Opens the full-page review/assign view on click. */}
      {status === 'created' && selected.size > 1 && !showSelected &&
        createPortal(
          <button
            onClick={() => setShowSelected(true)}
            title={`Review & assign ${selected.size} selected order${selected.size === 1 ? '' : 's'}`}
            className="group fixed right-0 z-[150] flex items-center gap-2 py-3 pl-4 pr-5 text-white font-extrabold text-xs cursor-pointer transition-all duration-200 hover:pr-7 animate-in slide-in-from-right-4"
            style={{ top: '70%', background: `linear-gradient(135deg, ${BRAND}, ${BRAND_LIGHT})`, borderTopLeftRadius: 9999, borderBottomLeftRadius: 9999, boxShadow: `0 10px 30px ${ring(BRAND)}` }}
          >
            <span className="relative inline-flex">
              <ClipboardList size={18} />
              <span className="absolute -top-2.5 -right-2.5 min-w-[17px] h-[17px] px-1 rounded-full bg-rose-500 text-[9px] font-black flex items-center justify-center ring-2 ring-white">{selected.size}</span>
            </span>
            <span className="max-w-0 overflow-hidden group-hover:max-w-[80px] transition-all duration-200 whitespace-nowrap">Review</span>
          </button>,
          document.body,
        )}

      {showSelected &&
        createPortal(
          <SelectedOrdersPage
            rows={selectedRows}
            rowKey={rowKey}
            riderOptions={riderOptions}
            ridersLoading={ridersQ.isLoading}
            assignRiderId={assignRiderId}
            setAssignRiderId={setAssignRiderId}
            assigning={assignMut.isPending}
            assignMsg={assignMsg}
            onAssign={handleAssign}
            onRemove={(k) => toggleRow(k)}
            onClose={() => setShowSelected(false)}
          />,
          document.body,
        )}
    </div>
  );
}

const DIVIDER_C = '#f1f5f9';

// ── Selected-orders review page (opened from the right-edge floating badge) ──────
function SelectedOrdersPage({
  rows, rowKey, riderOptions, ridersLoading, assignRiderId, setAssignRiderId, assigning, assignMsg, onAssign, onRemove, onClose,
}: {
  rows: Row[];
  rowKey: (r: Row) => string;
  riderOptions: { id: number; label: string }[];
  ridersLoading: boolean;
  assignRiderId: number;
  setAssignRiderId: (n: number) => void;
  assigning: boolean;
  assignMsg: string;
  onAssign: () => void;
  onRemove: (k: string) => void;
  onClose: () => void;
}) {
  return (
    <div className="fixed inset-0 z-[200] overflow-y-auto animate-in fade-in duration-200" style={{ background: '#f8fafc' }}>
      {/* Sticky page header with the assign controls */}
      <div className="sticky top-0 z-10 border-b" style={{ background: '#fff', borderColor: BORDER }}>
        <div className="max-w-5xl mx-auto px-4 md:px-8 py-4 flex flex-wrap items-center gap-3">
          <button onClick={onClose} className="inline-flex items-center gap-1.5 rounded-full font-bold text-xs cursor-pointer" style={{ padding: '8px 14px', border: `1px solid ${BORDER}`, color: TEXT_2, background: '#fff' }}>
            <ArrowLeft size={14} /> Back to orders
          </button>
          <div className="flex items-center gap-2">
            <span className="h-9 w-9 rounded-xl flex items-center justify-center" style={{ background: tint(BRAND), color: BRAND }}><ClipboardList size={18} /></span>
            <div>
              <h1 className="font-bold text-lg tracking-tight leading-none" style={{ color: TEXT }}>Selected Orders</h1>
              <p className="text-[11px] mt-1" style={{ color: TEXT_2 }}>{rows.length} order{rows.length === 1 ? '' : 's'} ready to assign</p>
            </div>
          </div>
          <div className="flex items-center gap-2 ml-auto">
            <select value={assignRiderId} onChange={(e) => setAssignRiderId(Number(e.target.value))} className="rounded-full font-bold text-xs outline-none cursor-pointer" style={{ padding: '8px 12px', border: `1.5px solid ${edge(BRAND)}`, background: '#fff', color: BRAND, maxWidth: 260 }}>
              <option value={0}>{ridersLoading ? 'Loading riders…' : riderOptions.length ? 'Select rider…' : 'No riders available'}</option>
              {riderOptions.map((o) => <option key={o.id} value={o.id}>{o.label}</option>)}
            </select>
            <button onClick={onAssign} disabled={!assignRiderId || rows.length === 0 || assigning} className="inline-flex items-center gap-1.5 rounded-full font-extrabold text-white cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed" style={{ padding: '8px 16px', fontSize: 12, background: `linear-gradient(135deg, ${BRAND}, ${BRAND_LIGHT})`, boxShadow: `0 6px 18px ${ring(BRAND)}` }}>
              {assigning ? <Loader2 size={14} className="animate-spin" /> : <UserCheck size={14} />} Assign rider
            </button>
          </div>
        </div>
      </div>

      <div className="max-w-5xl mx-auto px-4 md:px-8 py-6">
        {assignMsg && <div className="mb-4 rounded-xl px-4 py-2.5 text-xs font-semibold" style={{ background: tint(BRAND), border: `1px solid ${edge(BRAND)}`, color: BRAND }}>{assignMsg}</div>}
        {rows.length === 0 ? (
          <div className="bg-white border rounded-2xl p-12 text-center text-xs" style={{ borderColor: BORDER, color: TEXT_3 }}>
            No orders selected. <button onClick={onClose} className="font-bold underline cursor-pointer" style={{ color: BRAND }}>Go back</button>
          </div>
        ) : (
          <div className="bg-white border overflow-hidden" style={{ borderColor: BORDER }}>
            <div className="overflow-x-auto">
              <table className="w-full" style={{ minWidth: 720 }}>
                <thead><tr>{['#', 'Order', 'Pickup', 'Drop', 'Status', ''].map((h, i) => <th key={i} className="px-3 py-2.5 text-left" style={TH_STYLE}>{h}</th>)}</tr></thead>
                <tbody>
                  {rows.map((r, i) => {
                    const st = fstr(r.orderstatus).toLowerCase();
                    return (
                      <tr key={rowKey(r) || i} style={{ borderBottom: `1px solid ${DIVIDER_C}` }}>
                        <td className="px-3 py-2.5 font-mono" style={{ color: TEXT_3 }}>{i + 1}</td>
                        <td className="px-3 py-2.5">
                          <p className="font-extrabold font-mono text-[13px]" style={{ color: TEXT }}>{fstr(r.orderid) || `#${fstr(r.orderheaderid)}`}</p>
                          <p className="text-[10px]" style={{ color: TEXT_2 }}>{shortTime(r.orderdate || r.deliverydate)}</p>
                        </td>
                        <td className="px-3 py-2.5">
                          <p className="font-bold text-[12px] truncate max-w-[180px]" style={{ color: TEXT }}>{fstr(r.pickupcustomer) || fstr(r.tenantname) || '—'}</p>
                          <p className="text-[10px] truncate max-w-[180px]" style={{ color: TEXT_2 }}>{fstr(r.pickupsuburb) || fstr(r.pickupaddress)}</p>
                        </td>
                        <td className="px-3 py-2.5">
                          <p className="font-bold text-[12px] truncate max-w-[180px]" style={{ color: TEXT }}>{fstr(r.deliverycustomer) || '—'}</p>
                          <p className="text-[10px] truncate max-w-[180px]" style={{ color: TEXT_2 }}>{fstr(r.deliverysuburb) || fstr(r.deliveryaddress)}</p>
                        </td>
                        <td className="px-3 py-2.5"><StatusChip label={st || '—'} color={statusColor(ORDER_STATUS, st)} /></td>
                        <td className="px-3 py-2.5 text-right">
                          <button onClick={() => onRemove(rowKey(r))} title="Remove from selection" className="p-1 rounded-full cursor-pointer" style={{ color: TEXT_3 }}><X size={15} /></button>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function TotalChip({ label, value, color }: { label: string; value: string; color: string }) {
  return (
    <span className="inline-flex items-center gap-1.5 rounded-full font-bold" style={{ padding: '4px 11px', fontSize: 11.5, background: soft(color), color, border: `1px solid ${edge(color)}` }}>
      <span className="uppercase tracking-wider text-[9px] font-extrabold opacity-80">{label}</span>
      <span className="font-mono">{value}</span>
    </span>
  );
}

function PagerBtn({ children, disabled, onClick }: { children: React.ReactNode; disabled?: boolean; onClick: () => void }) {
  return (
    <button onClick={onClick} disabled={disabled}
      className="inline-flex items-center gap-1 rounded-full font-bold transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
      style={{ padding: '6px 12px', fontSize: 11, border: `1px solid ${BORDER}`, background: '#fff', color: TEXT_2 }}>
      {children}
    </button>
  );
}

// ── Order details modal ─────────────────────────────────────────────────────────
function OrderDetailModal({ order, onClose }: { order: Row; onClose: () => void }) {
  const orderheaderid = order.orderheaderid ?? order.orderid;
  const detailsQ = useFiestaOrderDetails(orderheaderid as number | string);
  const lines = (detailsQ.data ?? []).map((row) => {
    const quantity = fnum(row.quantity) || fnum(row.qty) || fnum(row.orderqty);
    const price = fnum(row.price) || fnum(row.unitprice) || fnum(row.retailprice);
    return { name: fstr(row.productname) || fstr(row.itemname) || 'Item', quantity, price, lineTotal: fnum(row.amount) || fnum(row.productsumprice) || price * quantity };
  });
  const st = fstr(order.orderstatus).toLowerCase();
  const total = fnum(order.deliveryamt) || fnum(order.orderamount);

  // Portal to <body> so the overlay escapes any transformed / blurred / overflow
  // ancestor in the view tree — otherwise `fixed inset-0` resolves against that
  // ancestor (not the viewport) and the panel collapses to a sliver. The explicit
  // viewport-relative width is a belt-and-suspenders so sizing never depends on
  // percentage resolution against a broken containing block.
  return createPortal(
    <div className="fixed inset-0 z-[200] flex items-center justify-center p-4" style={{ background: 'rgba(15,23,42,0.4)', backdropFilter: 'blur(4px)' }}
      onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="bg-white max-h-[90vh] flex flex-col overflow-hidden animate-in zoom-in-95 duration-200" style={{ width: 'min(32rem, 92vw)', border: `1px solid ${BORDER}`, boxShadow: '0 18px 50px rgba(15,23,42,0.18)' }}>
        <div style={{ height: 4, background: `linear-gradient(90deg, ${BRAND} 0%, ${soft(BRAND)} 100%)` }} />
        <div className="p-4 border-b flex justify-between items-center shrink-0" style={{ borderColor: BORDER, background: SURFACE_ALT }}>
          <h4 className="font-extrabold flex items-center gap-2" style={{ color: TEXT }}><Package size={16} style={{ color: BRAND }} /> Order {fstr(order.orderid) || `#${fstr(order.orderheaderid)}`}</h4>
          <button onClick={onClose} className="p-1 rounded-full cursor-pointer" style={{ color: TEXT_3 }}><X size={16} /></button>
        </div>
        <div className="p-4 space-y-4 overflow-y-auto flex-1">
          <div className="flex items-center justify-between">
            <StatusChip label={st || '—'} color={statusColor(ORDER_STATUS, st)} />
            <span className="text-[11px] font-medium" style={{ color: TEXT_2 }}>{shortTime(order.orderdate || order.deliverydate)}</span>
          </div>
          <div className="p-3 rounded-xl space-y-1.5" style={{ background: SURFACE_ALT, border: `1px solid ${BORDER}` }}>
            <div className="flex items-center gap-2 font-bold" style={{ color: TEXT }}>{fstr(order.deliverycustomer) || 'Customer'}</div>
            {fstr(order.deliverycontactno) && <div className="flex items-center gap-2 font-mono text-xs" style={{ color: TEXT_2 }}><Phone size={12} /> {fstr(order.deliverycontactno)}</div>}
            <div className="flex items-start gap-2 text-xs" style={{ color: TEXT_2 }}><MapPin size={12} className="mt-0.5 shrink-0" /> <span className="leading-relaxed">{fstr(order.deliveryaddress) || fstr(order.deliverysuburb) || 'Address unavailable'}</span></div>
          </div>
          <div>
            <span className="text-[10px] font-extrabold uppercase tracking-wide block mb-2" style={{ color: TEXT_2 }}>Order Items</span>
            <div className="rounded-xl p-3" style={{ background: 'rgba(248,250,252,0.6)', border: `1px solid ${BORDER}` }}>
              {detailsQ.isLoading && <div className="py-2 flex items-center gap-1.5 text-[11px] font-medium" style={{ color: TEXT_3 }}><Loader2 size={12} className="animate-spin" /> Loading line items…</div>}
              {!detailsQ.isLoading && lines.length === 0 && <div className="py-2 text-[11px] font-medium" style={{ color: TEXT_3 }}>No line items returned for this order.</div>}
              {lines.map((item, idx) => (
                <div key={idx} className="py-2 flex justify-between items-center" style={{ borderTop: idx ? `1px solid ${DIVIDER_C}` : undefined }}>
                  <div><p className="font-bold text-xs" style={{ color: TEXT }}>{item.name}</p><p className="text-[10px]" style={{ color: TEXT_2 }}>Qty: {item.quantity} × ₹{item.price}</p></div>
                  <span className="font-extrabold font-mono text-xs" style={{ color: TEXT }}>₹{item.lineTotal.toLocaleString('en-IN')}</span>
                </div>
              ))}
              {total > 0 && (
                <div className="pt-2 mt-1 flex justify-between items-center font-extrabold text-sm" style={{ color: BRAND, borderTop: `1px dashed ${BORDER}` }}>
                  <span>Order Total</span><span className="font-mono">₹{total.toLocaleString('en-IN')}</span>
                </div>
              )}
            </div>
          </div>
        </div>
        <div className="p-3 border-t flex justify-end shrink-0" style={{ borderColor: BORDER, background: SURFACE_ALT }}>
          <button onClick={onClose} className="rounded-full font-bold cursor-pointer text-white" style={{ padding: '8px 16px', background: `linear-gradient(135deg, ${BRAND}, ${BRAND_LIGHT_LOCAL})` }}>Close</button>
        </div>
      </div>
    </div>,
    document.body,
  );
}

const BRAND_LIGHT_LOCAL = '#9255AB';
