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

/**
 * Dispatch cockpit — integrates live deliveries, rider assignments, and route
 * visualization. Reuses the dispatch page stylesheet from the operations console
 * and reproduces its DOM structure: header, view-mode tabs, sidebar (KPI + group
 * cards), and map centrepiece with Leaflet.
 *
 * Features:
 *   • Group deliveries by rider, zone, location, or status
 *   • Focus on a specific group to see its trip blocks and detailed order cards
 *   • Map-based route visualization with planned stops (actual GPS awaiting backend)
 *   • Real-time KPI cards (orders, riders, completion %)
 *   • Date navigation for historical dispatch view
 */

import React, { useMemo, useState, useEffect } from 'react';
import {
  Map as MapIcon,
  MapPin,
  Bike,
  Users,
  Phone,
  Store,
  Truck,
  Package,
  Ruler,
  Wallet,
  Crosshair,
  Clock,
  Utensils,
  Mailbox,
  StickyNote,
  ArrowLeftRight,
  ChevronLeft,
  ChevronRight,
  List,
  Play,
} from 'lucide-react';
import {
  useFiestaDeliveries,
  useFiestaTenantLocations,
  useFiestaTenantCustomers,
} from '../services/fiestaQueries';
import {
  FIESTA_TENANT_ID,
  num as fnum,
  str as fstr,
  customerName,
  customerStoreId,
  type Row,
} from '../services/fiestaApi';
import { colorFor } from '../services/dispatchShared';
import DispatchMap, { type MapPoint } from './DispatchMap';
import CustomerDetailPanel from './CustomerDetailPanel';
import './DispatchView.css';

// Legacy direct utilities (will be migrated to dispatchShared)
const STATUS_HEX: Record<string, string> = {
  pending: '#f59e0b',
  accepted: '#6366f1',
  arrived: '#06b6d4',
  picked: '#8b5cf6',
  active: '#14b8a6',
  skipped: '#f97316',
  delivered: '#22c55e',
  cancelled: '#ef4444',
};

function statusStyle(s: string): React.CSSProperties {
  const hex = STATUS_HEX[s.toLowerCase()] || '#64748b';
  return { background: `${hex}1f`, color: hex };
}

/**
 * Best available area label for a delivery row. `getdeliveries` has NO
 * `deliverysuburb` column — reading it left every "areas" count at 0 and hid the
 * suburb strip entirely. The feed does carry `locationsuburb` and a full
 * `deliveryaddress`, so fall back to the address's locality segment
 * ("12 Main St, Peelamedu, Coimbatore, 641004" → "Coimbatore").
 */
function areaOf(r: Row): string {
  const direct = fstr(r.deliverysuburb).trim() || fstr(r.locationsuburb).trim();
  if (direct) return direct;
  const parts = fstr(r.deliveryaddress)
    .split(',')
    .map((p) => p.trim())
    .filter(Boolean)
    // Trailing postcode / country segments aren't areas.
    .filter((p) => !/^\d{4,6}$/.test(p) && p.toLowerCase() !== 'india');
  return parts.length > 1 ? parts[parts.length - 1] : '';
}

/** Drop coordinates from a delivery row (several field spellings), or null. */
function dropLatLon(r: Row): [number, number] | null {
  const lat = fnum(r.droplat) || fnum(r.deliverylat) || fnum(r.deliverylatitude);
  const lon = fnum(r.droplon) || fnum(r.deliverylong) || fnum(r.deliverylon) || fnum(r.deliverylongitude);
  return lat && lon ? [lat, lon] : null;
}

/** Pickup/hub coordinates from a delivery row, or null. */
function pickupLatLon(r: Row): [number, number] | null {
  const lat = fnum(r.pickuplat) || fnum(r.pickuplatitude);
  const lon = fnum(r.pickuplong) || fnum(r.picklongitude) || fnum(r.pickuplon);
  return lat && lon ? [lat, lon] : null;
}

// ── View modes ──────────────────────────────────────────────────────────────────
// The tab row lives in DispatchHubView; only these two modes are reachable.
type ViewMode = 'stores' | 'customers';

interface Group {
  id: string;
  name: string;
  color: string;
  orders: Row[];
  delivered: number;
  totalKm: number;
  profit: number;
  riders: Set<string>;
  suburbs: Map<string, number>;
  statusCounts: Record<string, number>;
  raw?: any;
  /** Customer view: the outlet this customer is registered against, for the card badge. */
  storeName?: string;
}

interface DispatchViewProps {
  locationid?: number;
  tenantId?: number;
  date: string;
  viewMode: ViewMode;
}

export default function DispatchView({ locationid, tenantId = FIESTA_TENANT_ID, date, viewMode }: DispatchViewProps) {
  const [focusedId, setFocusedId] = useState<string | null>(null);

  useEffect(() => {
    setFocusedId(null);
  }, [viewMode]);

  const [customerStoreFilter, setCustomerStoreFilter] = useState<string>('all');
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
  const [tripSort, setTripSort] = useState<'planned' | 'time'>('planned');
  const [animateNonce, setAnimateNonce] = useState(0);
  const [animating, setAnimating] = useState(false);

  // Core dispatch data
  const deliveriesQ = useFiestaDeliveries({ tenantid: tenantId, fromdate: date, todate: date, locationid });
  const locationsQ = useFiestaTenantLocations(tenantId);
  // Customers. Admin (no locationid) gets every customer under the tenant and
  // filters client-side; a store user sends its locationid so the backend scopes
  // to that outlet server-side via `tenantcustomers.locationid`.
  const customersQ = useFiestaTenantCustomers({ tenantid: tenantId, locationid: locationid || 0 });

  // Live deliveries only — no sample/demo fallback. When the feed is empty the
  // cockpit shows a genuine empty state rather than fabricated riders/stops.
  const allRows = deliveriesQ.data ?? [];
  const inScope = (r: Row) => !locationid || fnum(r.locationid) === locationid;

  const rows = useMemo(
    () => allRows.filter(inScope),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [allRows, locationid],
  );

  /** locationid → outlet name, for stamping a store badge on customer cards. */
  const storeNames = useMemo(() => {
    const m = new Map<number, string>();
    for (const loc of locationsQ.data ?? []) {
      const id = fnum(loc.locationid);
      if (id) m.set(id, fstr(loc.locationname).trim() || `Store ${id}`);
    }
    return m;
  }, [locationsQ.data]);

  // ── Grouping ────────────────────────────────────────────────────────────────
  // Both modes seed from the roster first (every outlet / every customer under
  // the scope) and then fold the day's deliveries into those seeds. That's what
  // makes an outlet with no deliveries today, or a customer who hasn't ordered
  // today, still appear in the list rather than vanishing.
  const groups = useMemo<Group[]>(() => {
    const map = new Map<string, Group>();
    const blank = (id: string, name: string): Group => ({
      id,
      name,
      color: colorFor(id),
      orders: [],
      delivered: 0,
      totalKm: 0,
      profit: 0,
      riders: new Set(),
      suburbs: new Map(),
      statusCounts: {},
    });

    if (viewMode === 'stores' && locationsQ.data) {
      // Admin: every outlet under the tenant. Store user: only its own, because
      // `locationid` is set.
      for (const loc of locationsQ.data) {
        if (locationid && fnum(loc.locationid) !== locationid) continue;
        const id = String(fnum(loc.locationid)).toLowerCase();
        map.set(id, blank(id, fstr(loc.locationname) || `Store ${id}`));
      }
    }

    if (viewMode === 'customers' && customersQ.data) {
      for (const cust of customersQ.data) {
        const storeId = customerStoreId(cust);
        // Admin's store dropdown. A store user never sees it (the backend has
        // already scoped the list), so this only ever narrows the admin view.
        if (customerStoreFilter !== 'all' && String(storeId) !== customerStoreFilter) continue;
        const id = String(fnum(cust.customerid) || fstr(cust.contactno)).toLowerCase();
        map.set(id, {
          ...blank(id, customerName(cust) || `Customer ${id}`),
          raw: cust,
          storeName: storeNames.get(storeId),
        });
      }
    }

    const keyOf = (r: Row): { id: string; name: string } => {
      if (viewMode === 'stores') {
        const locId = fstr(r.locationid) || fstr(r.pickuplocationid) || 'unknown';
        const name = fstr(r.pickupcustomer) || fstr(r.pickuplocation) || `Store ${locId}`;
        return { id: locId.toLowerCase(), name };
      }
      const custId = fstr(r.customerid) || fstr(r.deliverycontactno) || 'unknown';
      return { id: custId.toLowerCase(), name: customerName(r) || `Customer ${custId}` };
    };

    for (const r of rows) {
      const { id, name } = keyOf(r);
      let g = map.get(id);
      if (!g) {
        // A delivery whose customer isn't in the roster (not linked to this
        // tenant, or beyond the page we fetched). Keep it — dropping it would
        // hide real work from the board — but honour an active store filter.
        if (viewMode === 'customers' && customerStoreFilter !== 'all') continue;
        g = blank(id, name);
        if (viewMode === 'customers') {
          // Stand in for the missing customer record so the card still opens.
          // Without a `raw` the detail panel stayed on "Select a customer" no
          // matter how many times the card was clicked.
          g.raw = {
            customerid: fnum(r.customerid),
            firstname: fstr(r.deliverycustomer),
            contactno: fstr(r.deliverycontactno),
            address: fstr(r.deliveryaddress),
            tenantlocationid: fnum(r.locationid),
            locationname: fstr(r.locationname),
          };
          g.storeName = storeNames.get(fnum(r.locationid)) || fstr(r.locationname) || undefined;
        }
        map.set(id, g);
      }
      g.orders.push(r);
      const st = fstr(r.orderstatus).toLowerCase();
      if (st === 'delivered') g.delivered += 1;
      g.statusCounts[st] = (g.statusCounts[st] ?? 0) + 1;
      g.totalKm += fnum(r.kms);
      g.profit += fnum(r.profit);
      const rid = fstr(r.userid) || fstr(r.ridername);
      if (rid) g.riders.add(rid);
      const sub = areaOf(r);
      if (sub) g.suburbs.set(sub, (g.suburbs.get(sub) ?? 0) + 1);
    }
    return Array.from(map.values()).sort((a, b) => b.orders.length - a.orders.length || a.name.localeCompare(b.name));
  }, [rows, viewMode, locationid, locationsQ.data, customersQ.data, customerStoreFilter, storeNames]);

  useEffect(() => {
    if (viewMode === 'stores' && locationid && groups.length === 1 && !focusedId) {
      setFocusedId(groups[0].id);
    }
  }, [viewMode, locationid, groups, focusedId]);

  const focused = groups.find((g) => g.id === focusedId) ?? null;

  // Trip blocks for the focused group, one per rider.
  const tripBlocks = useMemo(() => {
    if (!focused) return [];
    const map = new Map<string, { label: string; color: string; orders: Row[] }>();
    for (const r of focused.orders) {
      const rid = fstr(r.userid) || fstr(r.ridername) || 'unassigned';
      const label = fstr(r.ridername) || (rid === 'unassigned' ? 'Unassigned' : `Rider ${rid}`);
      let blk = map.get(rid);
      if (!blk) { blk = { label, color: colorFor(rid), orders: [] }; map.set(rid, blk); }
      blk.orders.push(r);
    }
    const blocks = Array.from(map.values());
    for (const blk of blocks) {
      blk.orders.sort((a, b) => {
        if (tripSort === 'time') {
          const ta = fstr(a.deliverytime) || fstr(a.expecteddeliverytime);
          const tb = fstr(b.deliverytime) || fstr(b.expecteddeliverytime);
          return ta.localeCompare(tb);
        }
        // `step` isn't in the getdeliveries response, so planned order falls back
        // to assignment time. Kept in case the backend starts sending it.
        const sa = fnum(a.step);
        const sb = fnum(b.step);
        if (sa && sb && sa !== sb) return sa - sb;
        return fstr(a.assigntime).localeCompare(fstr(b.assigntime));
      });
    }
    return blocks;
  }, [focused, tripSort]);

  // Map points: the focused group's ordered stops (with a route), else every stop
  // for the day (coloured per rider). Rows without coordinates are skipped.
  const mapPoints = useMemo<MapPoint[]>(() => {
    const src = focused ? tripBlocks.flatMap((b) => b.orders) : rows;
    const out: MapPoint[] = [];
    src.forEach((r, i) => {
      const ll = dropLatLon(r);
      if (!ll) return;
      out.push({
        id: fstr(r.deliveryid) || fstr(r.orderid) || String(i),
        lat: ll[0],
        lon: ll[1],
        step: fnum(r.step) || i + 1,
        color: focused ? focused.color : colorFor(fstr(r.userid) || fstr(r.ridername) || 'x'),
        title: customerName(r) || `Order ${fstr(r.orderid)}`,
        subtitle: areaOf(r) || fstr(r.deliveryaddress),
        status: fstr(r.orderstatus),
        raw: r,
      });
    });
    return out;
  }, [focused, tripBlocks, rows]);

  // Route start = the focused group's pickup/hub (so the road route originates there).
  const firstOrder = tripBlocks[0]?.orders[0] ?? focused?.orders[0];
  const routeStart = focused && firstOrder ? pickupLatLon(firstOrder) : null;

  // The roster query matters as much as the delivery feed — gating on deliveries
  // alone flashed "No customers found" while the customer list was still loading.
  const isLoading =
    deliveriesQ.isLoading || (viewMode === 'customers' ? customersQ.isLoading : locationsQ.isLoading);

  // KPI scope.
  const totalOrders = rows.length;
  const totalDelivered = rows.filter((r) => fstr(r.orderstatus).toLowerCase() === 'delivered').length;

  const fmtTime = (raw: unknown): string => {
    const m = fstr(raw).match(/(\d{1,2}):(\d{2})/);
    return m ? `${m[1]}:${m[2]}` : '';
  };

  return (
    <div style={{ height: '100%', minHeight: 0 }}>
      <div className="dispatch-container embedded">
        {/* ── Body ── */}
        <div id="body" className={sidebarCollapsed ? 'sidebar-collapsed' : ''}>
          <button
            className={`sidebar-toggle-tab${sidebarCollapsed ? ' is-collapsed' : ''}`}
            onClick={() => setSidebarCollapsed((c) => !c)}
            title={sidebarCollapsed ? 'Show panel' : 'Hide panel'}
          >
            {sidebarCollapsed ? <ChevronRight size={26} /> : <ChevronLeft size={18} />}
          </button>

          {/* Sidebar */}
          <div id="sidebar">
            <div className="sb-header">
              <div className="sb-header-top">
                <div className="sb-header-title">
                  <span className="sb-title-bar" aria-hidden="true" />
                  <span className="sb-title-text">CONSOLE</span>
                </div>
                <span className="sb-header-scope">
                  <span className="sb-scope-dot" />
                  {totalOrders} stops
                </span>
              </div>
              <div className="sb-header-tiles">
                <div className="sb-tile sb-tile-orders">
                  <span className="sb-tile-icon"><Package size={16} /></span>
                  <div className="sb-tile-body">
                    <div className="sb-tile-value">{totalOrders}</div>
                    <div className="sb-tile-label">Orders</div>
                  </div>
                </div>
                <div className="sb-tile sb-tile-deliveries">
                  <span className="sb-tile-icon">
                    <Truck size={16} />
                  </span>
                  <div className="sb-tile-body">
                    <div className="sb-tile-value">
                      {`${totalDelivered}/${totalOrders}`}
                    </div>
                    <div className="sb-tile-label">
                      Deliveries
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <div id="riders-panel">
              {isLoading ? (
                <div className="ph">{viewMode === 'customers' ? 'Loading customers…' : 'Loading dispatch feed…'}</div>
              ) : focused && viewMode !== 'customers' ? (
                <FocusedDetail
                  focused={focused}
                  tripBlocks={tripBlocks}
                  tripSort={tripSort}
                  setTripSort={setTripSort}
                  onBack={(locationid && viewMode === 'stores' && groups.length === 1) ? undefined : () => setFocusedId(null)}
                  fmtTime={fmtTime}
                />
              ) : groups.length === 0 ? (
                <div className="ph">
                  {viewMode === 'customers'
                    ? (customerStoreFilter !== 'all' ? 'No customers for this store' : 'No customers found')
                    : 'No stores found'}
                </div>
              ) : (
                <>
                  <div className="ph relative flex items-center justify-between w-full h-8">
                    <span>{viewMode === 'customers' ? 'Customers' : 'Stores'} ({groups.length})</span>
                    {viewMode === 'customers' && !locationid && locationsQ.data && (
                      <div className="absolute right-0 flex items-center group/filter cursor-pointer">
                        <select 
                          className="appearance-none w-36 text-[11px] font-extrabold tracking-wide rounded-md pl-3 pr-8 py-1.5 bg-white text-slate-800 shadow-sm ring-1 ring-slate-900/5 hover:bg-slate-50 outline-none focus:ring-2 focus:ring-[#662582]/30 transition-all duration-300 cursor-pointer relative z-0 truncate"
                          style={{ textOverflow: 'ellipsis' }}
                          value={customerStoreFilter}
                          onChange={(e) => {
                            setCustomerStoreFilter(e.target.value);
                            setFocusedId(null);
                          }}
                        >
                          <option value="all">All Stores</option>
                          {locationsQ.data.map(loc => (
                            <option key={fnum(loc.locationid)} value={String(fnum(loc.locationid))}>
                              {fstr(loc.locationname) || `Store ${fnum(loc.locationid)}`}
                            </option>
                          ))}
                        </select>
                        <div className="absolute right-2 text-slate-400 pointer-events-none group-hover/filter:text-[#662582] transition-colors">
                          <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>
                        </div>
                      </div>
                    )}
                  </div>
                  {groups.map((g) => (
                    <React.Fragment key={g.id}>
                      {viewMode === 'customers' ? (
                        <CustomerCard g={g} onClick={() => setFocusedId(g.id)} isSelected={focusedId === g.id} />
                      ) : (
                        <StoreCard g={g} onClick={() => setFocusedId(g.id)} />
                      )}
                    </React.Fragment>
                  ))}
                </>
              )}
            </div>
          </div>

          {/* Main Content Area */}
          {viewMode === 'customers' ? (
            <div id="customer-panel-wrap" style={{ flex: 1, position: 'relative', zIndex: 50, backgroundColor: '#f8fafc', overflow: 'hidden', minWidth: 0 }}>
              <style>{`
                #customer-panel-wrap * {
                  margin: revert;
                  padding: revert;
                  box-sizing: border-box;
                }
              `}</style>
              <div className="relative h-full w-full flex flex-col">
                {focused?.raw ? (
                  <CustomerDetailPanel customer={focused.raw} onClose={() => setFocusedId(null)} />
                ) : (
                  <div className="flex-1 flex flex-col items-center justify-center text-center h-full w-full">
                    <div className="w-14 h-14 rounded-2xl bg-purple-50 border border-purple-100 text-[#662582] flex items-center justify-center mb-md">
                      <Users size={24} />
                    </div>
                    <p className="font-bold text-sm text-slate-900">Select a customer</p>
                    <p className="text-xs text-slate-500 mt-1">Choose a customer from the list to view their billing history.</p>
                  </div>
                )}
              </div>
            </div>
          ) : (
            <div id="map-wrap">
              {/* Live Leaflet route map */}
              <DispatchMap
                points={mapPoints}
                route={Boolean(focused)}
                routeColor={focused?.color || '#662582'}
                start={routeStart}
                resizeKey={`${sidebarCollapsed}|${viewMode}|${focusedId}`}
                animateNonce={animateNonce}
              />

              {/* Contextual note overlaid on the map */}
              {mapPoints.length === 0 ? (
                <div className="dmp-overlay-note">
                  <MapIcon size={13} /> No drop coordinates in {focused ? 'this route' : 'these deliveries'} yet.
                </div>
              ) : !focused ? (
                <div className="dmp-overlay-note">
                  <MapIcon size={13} /> Select a store to draw its route.
                </div>
              ) : null}

              {/* bottom-right overlay controls (gated) */}
              <div id="ov-br">
                <button
                  className={`sbt ${animating ? 'active' : ''}`}
                  disabled={!focused || mapPoints.length < 2}
                  onClick={() => {
                    if (!focused || mapPoints.length < 2) return;
                    setAnimating(true);
                    setAnimateNonce((n) => n + 1);
                    window.setTimeout(() => setAnimating(false), 2300);
                  }}
                  title={focused ? 'Replay the route draw' : 'Select a rider to animate its route'}
                >
                  <span className="sbt-icon"><Play size={14} /></span> {animating ? 'Animating…' : 'Animate Routes'}
                </button>
                <button className="sbt" disabled title="Planned-vs-actual compare needs rider GPS telemetry (awaiting backend)">
                  <span className="sbt-icon"><ArrowLeftRight size={14} /></span> Compare
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Store card ───────────────────────────────────────────────────────────────────
// One per outlet. Admin sees every outlet under the tenant, a store user sees
// only its own. Outlets with no deliveries today are deliberately still listed —
// "which of my stores is idle" is the question the admin list has to answer — so
// they render a quiet idle state instead of a row of zeroes.
function StoreCard({ g, onClick }: { g: Group; onClick: () => void }) {
  const suburbs = [...g.suburbs.entries()].sort((a, b) => b[1] - a[1]).map(([s]) => s);
  const idle = g.orders.length === 0;
  return (
    <div className={`rcard zone-card${idle ? ' is-idle' : ''}`} onClick={onClick}>
      <div className="zone-card-header">
        <div className="zone-card-emoji" style={{ color: idle ? '#94a3b8' : g.color }}><Store size={16} /></div>
        <div className="zone-card-titles">
          <div className="zone-card-name">{g.name}</div>
          <div className="zone-card-sub">
            {idle
              ? 'No deliveries today'
              : `${g.riders.size} rider${g.riders.size === 1 ? '' : 's'} · ${g.orders.length} ${g.orders.length === 1 ? 'delivery' : 'deliveries'}`}
          </div>
        </div>
        <span className="zone-card-arrow" aria-hidden="true">→</span>
      </div>
      {!idle && (
        <>
          <div className="zone-progress-row">
            <div className="zone-status-bar">
              {Object.entries(g.statusCounts).map(([s, c]) => (
                <div key={s} className="zone-status-seg" style={{ flex: c, background: STATUS_HEX[s] || '#cbd5e1' }} title={`${s}: ${c}`} />
              ))}
            </div>
            <div className="zone-progress-label">{g.delivered}/{g.orders.length}</div>
          </div>
          <div className="zone-stat-pills">
            {/* Only render a stat the feed can actually populate — the areas and
                profit pills used to sit at a permanent 0 for every store. */}
            {g.suburbs.size > 0 && (
              <span className="zone-stat-pill">
                <span className="zone-stat-icon"><MapPin size={12} /></span>
                <span className="zone-stat-value">{g.suburbs.size}</span>
                <span className="zone-stat-label">{g.suburbs.size === 1 ? 'area' : 'areas'}</span>
              </span>
            )}
            {g.totalKm > 0 && (
              <span className="zone-stat-pill">
                <span className="zone-stat-icon"><Ruler size={12} /></span>
                <span className="zone-stat-value">{g.totalKm.toFixed(0)}</span>
                <span className="zone-stat-label">km</span>
              </span>
            )}
            {g.profit > 0 && (
              <span className="zone-stat-pill">
                <span className="zone-stat-icon"><Wallet size={12} /></span>
                <span className="zone-stat-value">₹{g.profit.toLocaleString('en-IN')}</span>
                <span className="zone-stat-label">profit</span>
              </span>
            )}
          </div>
          {suburbs.length > 0 && (
            <div className="zone-card-suburbs">
              <span className="zone-card-suburbs-text">{suburbs.slice(0, 3).join(' · ')}</span>
              {suburbs.length > 3 && <span className="zone-card-suburbs-more">+{suburbs.length - 3}</span>}
            </div>
          )}
        </>
      )}
    </div>
  );
}

// ── Focused detail (trip blocks + order cards) ───────────────────────────────────
function FocusedDetail({
  focused,
  tripBlocks,
  tripSort,
  setTripSort,
  onBack,
  fmtTime,
}: {
  focused: Group;
  tripBlocks: Array<{ label: string; color: string; orders: Row[] }>;
  tripSort: 'planned' | 'time';
  setTripSort: (v: 'planned' | 'time') => void;
  onBack?: () => void;
  fmtTime: (raw: unknown) => string;
}) {
  return (
    <>
      {onBack && (
        <button className="sbt" onClick={onBack} style={{ marginBottom: 12 }}>
          <span className="sbt-icon"><ChevronLeft size={15} /></span> Back to list
        </button>
      )}

      {tripBlocks.length === 0 && (
        <div className="flex flex-col items-center justify-center p-8 text-center h-48 border-2 border-dashed border-slate-200 rounded-xl mt-4 bg-slate-50/50">
          <div className="w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center text-slate-400 mb-3">
            <Package size={20} />
          </div>
          <p className="font-bold text-slate-700 text-sm">No deliveries for {focused.name}</p>
          <p className="text-xs text-slate-500 mt-1">Nothing has been dispatched from this store on the selected date.</p>
        </div>
      )}

      {tripBlocks.map((blk, bi) => (
        <div className="trip-block" key={bi}>
          <div className="trip-header" style={{ background: `${blk.color}12`, borderColor: `${blk.color}40` }}>
            <span className="th-badge" style={{ background: blk.color }}>{blk.label}</span>
            <span className="trip-stats">
              <span><MapPin size={11} /> {blk.orders.length} stops</span>
              <span><Ruler size={11} /> {blk.orders.reduce((a, o) => a + fnum(o.kms), 0).toFixed(1)} km</span>
            </span>
            <div className="trip-sort-toggle" role="group">
              <button className={`trip-sort-pill ${tripSort === 'planned' ? 'is-active' : ''}`} onClick={() => setTripSort('planned')}>
                <List size={12} /> <span>Planned</span>
              </button>
              <button className={`trip-sort-pill ${tripSort === 'time' ? 'is-active' : ''}`} onClick={() => setTripSort('time')}>
                <Clock size={12} /> <span>By time</span>
              </button>
            </div>
          </div>

          <div className="zone-order-grid">
            {blk.orders.map((o, i) => {
              const st = fstr(o.orderstatus).toLowerCase();
              const step = fnum(o.step) || i + 1;
              const actual = fstr(o.deliverytime);
              const expected = fstr(o.expecteddeliverytime);
              const profit = fnum(o.profit);
              const km = fnum(o.kms);
              const charge = fnum(o.deliverycharge) || fnum(o.deliverycharges);
              return (
                <div className={`zone-order-card ${st === 'delivered' ? '' : 'is-pending-time'}`} key={fstr(o.deliveryid) || fstr(o.orderid) || i}>
                  <div className="zone-order-card-head">
                    <div className="zone-order-num" style={{ background: `${blk.color}15`, color: blk.color }}>{step}</div>
                    <div className="zone-order-id-block">
                      <div className="zone-order-id">Order #{fstr(o.orderid) || fstr(o.deliveryid)}</div>
                      {fstr(o.ridername) && (
                        <div className="zone-order-rider"><Bike size={10} /> {fstr(o.ridername)}</div>
                      )}
                    </div>
                    <div className="zone-order-status-stack">
                      {st && <span className="zone-order-status" style={statusStyle(st)}>{st}</span>}
                      {(actual || expected) && (
                        <span className={`zone-order-time ${actual ? '' : 'is-expected'}`}>
                          <Clock size={10} /> {fmtTime(actual || expected)}
                        </span>
                      )}
                    </div>
                  </div>

                  <div className="zone-order-customer"><Mailbox size={11} /> {customerName(o) || 'Customer'}</div>
                  {fstr(o.pickupcustomer) && (
                    <div className="zone-order-line"><Utensils size={11} /> {fstr(o.pickupcustomer)}</div>
                  )}
                  {fstr(o.deliveryaddress) && (
                    <div className="zone-order-line"><MapPin size={11} /> {fstr(o.deliveryaddress)}</div>
                  )}
                  {fstr(o.ordernotes) && (
                    <div className="zone-order-line zone-order-notes"><StickyNote size={11} /> {fstr(o.ordernotes)}</div>
                  )}

                  <div className="zone-order-stats">
                    <span className="zone-order-chip"><Ruler size={10} /> {km.toFixed(1)} km</span>
                    {profit !== 0 && (
                      <span className={`zone-order-chip ${profit < 0 ? 'is-loss' : 'is-profit'}`}>
                        <Wallet size={10} /> ₹{Math.abs(profit).toLocaleString('en-IN')}
                      </span>
                    )}
                    {charge > 0 && <span className="zone-order-chip">₹{charge} chg</span>}
                    <span className="zone-order-chip zone-order-trip"><Crosshair size={10} /> S{step}</span>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </>
  );
}

// ── Customer card ───────────────────────────────────────────────────────────────────
function CustomerCard({ g, onClick, isSelected }: { g: Group; onClick: () => void; isSelected?: boolean }) {
  const customer = g.raw;
  // g.name is already resolved through customerName(); reading `customername` off
  // the raw row (a column gettenantcustomers doesn't return) made every card in
  // the list read "Unknown Customer".
  const name = g.name;
  const phone = customer ? fstr(customer.contactno) || fstr(customer.phone) : '';
  const deliveries = g.orders.length;

  return (
    <div 
      className={`rcard transition-all duration-300 cursor-pointer overflow-hidden relative group ${
        isSelected
          ? 'border-purple-300 shadow-md bg-purple-50/50'
          : 'bg-white border-slate-200 hover:border-purple-200 hover:shadow-sm'
      }`}
      onClick={onClick}
    >
      {isSelected && (
        <div className="absolute left-0 top-0 bottom-0 w-1 bg-[#662582] rounded-l-md"></div>
      )}
      <div className="flex items-center gap-4 p-3.5 pl-4 border-none">
        <div className={`w-11 h-11 rounded-xl flex items-center justify-center shrink-0 transition-colors ${
          isSelected ? 'bg-[#662582] text-white shadow-md' : 'bg-slate-100 text-slate-500 group-hover:bg-purple-100 group-hover:text-[#662582]'
        }`}>
          <Users size={20} strokeWidth={isSelected ? 2 : 1.5} />
        </div>
        <div className="min-w-0 flex-1">
          <div className={`font-bold text-sm truncate tracking-tight ${isSelected ? 'text-[#0f172a]' : 'text-slate-800'}`}>
            {name || 'Unknown Customer'}
          </div>
          {phone && (
            <div className={`text-xs truncate font-medium mt-0.5 flex items-center gap-1 ${isSelected ? 'text-[#662582]' : 'text-slate-500'}`}>
              <Phone size={10} /> {phone}
            </div>
          )}
          {(g.storeName || deliveries > 0) && (
            <div className="mt-1.5 flex items-center gap-1.5 flex-wrap">
              {g.storeName && (
                <span className="inline-flex items-center gap-1 text-[10px] font-bold text-slate-500 bg-slate-100 px-1.5 py-0.5 rounded">
                  <Store size={9} /> {g.storeName}
                </span>
              )}
              {deliveries > 0 && (
                <span className="inline-flex items-center gap-1 text-[10px] font-bold text-emerald-700 bg-emerald-50 px-1.5 py-0.5 rounded">
                  <Truck size={9} /> {deliveries} today
                </span>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
