"use client";

import { useCallback, useEffect, useState } from "react";
import type { TelemetrySample } from "@/lib/api";
import {
  api,
  Assignment,
  AuditEntry,
  DeviceEvent,
  DeviceProfile,
  Investor,
  Vehicle,
  VehicleCommand,
  VehicleModel,
  VehicleType,
} from "@/lib/api";
import { useLocale } from "@/i18n/LocaleProvider";
import { useMapsConfig } from "@/lib/useMapsConfig";
import { useVehicleTypes } from "@/lib/useVehicleTypes";
import { HARDWARE_TYPES } from "@/lib/fleetCatalog";
import {
  Button,
  Card,
  ErrorState,
  Field,
  IconChip,
  SectionTitle,
  SegmentedControl,
  Select,
  SidePanel,
  StatusBadge,
  Numeric,
  Tone,
} from "@/components/ui";


/** Ensure a vehicle's existing (possibly legacy free-text) value is a selectable option. */
const withCurrent = (catalog: string[], current: string): string[] =>
  current && !catalog.includes(current) ? [current, ...catalog] : catalog;

// The full OMNI real-time control set (protocol §2.3.1), grouped by what an operator is doing.
const COMMAND_GROUPS: {
  label: string;
  items: { value: VehicleCommand; glyph: string; tone: Tone }[];
}[] = [
  {
    label: "vehicle.grpLock",
    items: [
      { value: "UNLOCK", glyph: "🔓", tone: "success" },
      { value: "LOCK", glyph: "🔒", tone: "danger" },
    ],
  },
  {
    label: "vehicle.grpFind",
    items: [
      { value: "LOCATE", glyph: "◎", tone: "info" },
      { value: "RING", glyph: "🔔", tone: "warning" },
      { value: "RING_OFF", glyph: "🔕", tone: "neutral" },
    ],
  },
  {
    label: "vehicle.grpLights",
    items: [
      { value: "HEADLIGHT_ON", glyph: "💡", tone: "warning" },
      { value: "HEADLIGHT_OFF", glyph: "🌙", tone: "neutral" },
      { value: "HEADLIGHT_FLASH", glyph: "✨", tone: "info" },
    ],
  },
  {
    label: "vehicle.grpPower",
    items: [
      { value: "THROTTLE_ON", glyph: "⚡", tone: "success" },
      { value: "THROTTLE_OFF", glyph: "⛔", tone: "neutral" },
      { value: "RESTART", glyph: "↻", tone: "info" },
      { value: "TRANSPORT_MODE", glyph: "🚚", tone: "neutral" },
      { value: "RESET_IOT", glyph: "⟳", tone: "danger" },
    ],
  },
];

/**
 * Everything ops does to one vehicle: inspect it, see where it is, drive the lock, set the owner,
 * and correct its details.
 */
export function VehicleDetail({
  vehicle,
  investors,
  assignment,
  onClose,
  onChanged,
}: {
  vehicle: Vehicle | null;
  investors: Investor[];
  assignment: Assignment | null;
  onClose: () => void;
  onChanged: () => void;
}) {
  const { t } = useLocale();
  const [tab, setTab] = useState<"details" | "control" | "owner" | "location" | "logs">("details");

  useEffect(() => {
    if (vehicle) setTab("details");
  }, [vehicle]);

  if (!vehicle) return null;

  return (
    <SidePanel open onClose={onClose} title={vehicle.qrCode}>
      <div className="mb-4 flex justify-center">
        <SegmentedControl
          value={tab}
          onChange={setTab}
          options={[
            { value: "details", label: t("vehicle.tabDetails") },
            { value: "control", label: t("vehicle.tabControl") },
            { value: "owner", label: t("vehicle.tabOwner") },
              { value: "location", label: t("vehicle.tabLocation") },
            { value: "logs", label: t("vehicle.tabLogs") },
          ]}
        />
      </div>

      {tab === "details" && <Details vehicle={vehicle} onChanged={onChanged} />}
      {tab === "control" && <Control vehicle={vehicle} onChanged={onChanged} />}
      {tab === "logs" && <Logs vehicle={vehicle} />}
        {tab === "location" && <Location vehicle={vehicle} />}
      {tab === "owner" && (
        <Owner
          vehicle={vehicle}
          investors={investors}
          assignment={assignment}
          onChanged={onChanged}
        />
      )}
    </SidePanel>
  );
}

/* ------------------------------------------------------------------- details */

function Details({
  vehicle,
  onChanged,
}: {
  vehicle: Vehicle;
  onChanged: () => void;
}) {
  const { t } = useLocale();
  const vehicleTypes = useVehicleTypes();
  const [editing, setEditing] = useState(false);
  const [form, setForm] = useState({
    qrCode: vehicle.qrCode,
    vehicleType: vehicle.vehicleType,
    imei: vehicle.imei ?? "",
    hardwareType: vehicle.hardwareType ?? "",
    vehicleModelId: vehicle.vehicleModelId ?? "",
    iotProfile: vehicle.iotProfile ?? "",
    manualLockNumber: vehicle.manualLockNumber ?? "",
    manualLockCode: vehicle.manualLockCode ?? "",
  });
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // The catalogues behind the three dropdowns. Loaded only while editing, since a read-only view
  // needs none of them.
  const [models, setModels] = useState<VehicleModel[]>([]);
  const [profiles, setProfiles] = useState<DeviceProfile[]>([]);
  const [spareImeis, setSpareImeis] = useState<string[]>([]);

  useEffect(() => {
    if (!editing) return;
    api.vehicleModels().then(setModels).catch(() => setModels([]));
    api.deviceProfiles().then(setProfiles).catch(() => setProfiles([]));
    // Only spares, plus whatever this vehicle already has: suggesting a device fitted to another
    // vehicle would invite creating a duplicate the server then refuses.
    api
      .iotDevices()
      .then((rows) =>
        setSpareImeis(
          rows
            .filter((r) => !r.assignedToQrCode || r.device.imei === vehicle.imei)
            .map((r) => r.device.imei),
        ),
      )
      .catch(() => setSpareImeis([]));
  }, [editing, vehicle.imei]);

  const save = async (e: React.FormEvent) => {
    e.preventDefault();
    setBusy(true);
    setError(null);
    try {
      await api.updateVehicle(vehicle.id, form);
      setEditing(false);
      onChanged();
    } catch (err) {
      setError(err instanceof Error ? err.message : t("common.error"));
    } finally {
      setBusy(false);
    }
  };

  if (editing) {
    return (
      <form onSubmit={save} className="space-y-4">
        <Select
          label={t("fleet.colType")}
          value={form.vehicleType}
          onChange={(e) =>
            setForm({ ...form, vehicleType: e.target.value as VehicleType })
          }
          // Retired types are excluded, but a vehicle already filed under one keeps its option
          // so editing anything else about it does not silently re-type it.
          options={withCurrent(
            vehicleTypes.active.map((x) => x.code),
            form.vehicleType,
          ).map((code) => ({ value: code, label: vehicleTypes.label(code) }))}
        />
        <Field
          label={t("fleet.colCode")}
          value={form.qrCode}
          onChange={(e) => setForm({ ...form, qrCode: e.target.value })}
        />
        {/* The catalogued model, not free text. Selecting one also refreshes the display name
            server-side, so lists and exports need no join. */}
        <Select
          label={t("fleet.colModel")}
          value={form.vehicleModelId}
          onChange={(e) => {
            const next = e.target.value;
            const picked = models.find((m) => m.id === next);
            setForm((f) => ({
              ...f,
              vehicleModelId: next,
              // A model's usual protocol is a sensible default, but never overwrites a profile
              // the operator has already chosen for this specific vehicle.
              iotProfile:
                f.iotProfile || picked?.defaultIotProfile || f.iotProfile,
            }));
          }}
          options={models
            .filter((m) => m.active || m.id === form.vehicleModelId)
            .map((m) => ({ value: m.id, label: m.name }))}
          placeholder={vehicle.model ?? t("fleet.modelNotSet")}
        />
        <div>
          <Field
            label={t("fleet.colImei")}
            value={form.imei}
            onChange={(e) => setForm({ ...form, imei: e.target.value })}
            datalist={spareImeis}
            dir="ltr"
          />
          <p className="mt-1.5 text-xs text-muted">{t("vehicle.imeiHint")}</p>
        </div>
        <Select
          label={t("vehicle.iotProfile")}
          value={form.iotProfile}
          onChange={(e) => setForm({ ...form, iotProfile: e.target.value })}
          options={profiles.map((p) => ({
            value: p.id,
            label: `${p.vendor}${p.model ? " " + p.model : ""} (${p.transport})${
              p.status === "SUPPORTED" ? "" : " - " + t("wiz.profilePlanned")
            }`,
          }))}
          placeholder={t("wiz.profileDefault")}
        />
        <Select
          label={t("fleet.colHardware")}
          value={form.hardwareType}
          onChange={(e) => setForm({ ...form, hardwareType: e.target.value })}
          options={withCurrent(HARDWARE_TYPES, form.hardwareType)}
          placeholder={t("fleet.hardwareNotSet")}
        />
        {/* The fallback that keeps a vehicle with a dead module earning instead of parked. */}
        <div className="rounded-card border border-line bg-background p-4">
          <p className="mb-3 text-sm font-medium">{t("vehicle.manualLock")}</p>
          <div className="grid gap-4 sm:grid-cols-2">
            <Field
              label={t("vehicle.manualLockNumber")}
              value={form.manualLockNumber}
              onChange={(e) => setForm({ ...form, manualLockNumber: e.target.value })}
              dir="ltr"
            />
            <Field
              label={t("vehicle.manualLockCode")}
              value={form.manualLockCode}
              onChange={(e) => setForm({ ...form, manualLockCode: e.target.value })}
              dir="ltr"
            />
          </div>
          <p className="mt-2 text-xs text-muted">{t("vehicle.manualLockHint")}</p>
        </div>
        {error && (
          <div className="tint tone-danger rounded-card border p-3 text-sm">{error}</div>
        )}
        <div className="flex gap-3">
          <Button variant="primary" type="submit" disabled={busy} className="flex-1">
            {busy ? t("common.loading") : t("vehicle.save")}
          </Button>
          <Button type="button" onClick={() => setEditing(false)}>
            {t("common.cancel")}
          </Button>
        </div>
      </form>
    );
  }

  return (
    <div className="space-y-4">
      <div className="card divide-y divide-line">
        {/* The label, not the code: the code is an internal key an operator never chose. */}
        <Row label={t("fleet.colType")} value={vehicleTypes.label(vehicle.vehicleType)} />
        <Row label={t("fleet.colModel")} value={vehicle.model ?? "-"} />
        <Row
          label={t("fleet.colImei")}
          value={vehicle.imei ?? t("fleet.noDevice")}
          mono
        />
        <Row label={t("fleet.colHardware")} value={vehicle.hardwareType ?? "-"} />
        <Row label={t("vehicle.iotProfile")} value={vehicle.iotProfile ?? "-"} />
        <Row
          label={t("vehicle.manualLock")}
          value={
            vehicle.manualLockCode
              ? `${vehicle.manualLockNumber ?? "-"} / ${vehicle.manualLockCode}`
              : "-"
          }
        />
        <Row label={t("fleet.colBattery")} value={`${vehicle.batteryPercent}%`} mono />
        <Row
          label={t("fleet.colSeen")}
          value={
            vehicle.lastSeenAt
              ? new Date(vehicle.lastSeenAt).toLocaleString()
              : t("fleet.never")
          }
        />
      </div>

      <VehicleMap vehicle={vehicle} />

      <Button onClick={() => setEditing(true)} className="w-full">
        {t("vehicle.edit")}
      </Button>
    </div>
  );
}

function Row({
  label,
  value,
  mono,
}: {
  label: string;
  value: string;
  mono?: boolean;
}) {
  return (
    <div className="flex items-center justify-between px-4 py-3">
      <span className="text-sm text-muted">{label}</span>
      {mono ? (
        <Numeric className="text-sm">{value}</Numeric>
      ) : (
        <span className="text-sm font-medium">{value}</span>
      )}
    </div>
  );
}

/* ----------------------------------------------------------------------- map */

/**
 * Last reported position.
 *
 * Uses the Maps Embed API, which needs only an API key and no SDK, enough to answer "where is
 * this vehicle". A missing key degrades to coordinates plus a link out, rather than a dead grey
 * box, exactly as the Android map does.
 */
function VehicleMap({ vehicle }: { vehicle: Vehicle }) {
  const { t } = useLocale();
  // Key + provider from the vault at runtime (API Keys page). Provider-aware: Google uses the
  // Embed API (iframe), Mapbox uses the Static Images API (a plain <img>). Both keyed at runtime.
  const { config } = useMapsConfig();
  const provider = config?.provider ?? null;
  const apiKey = config?.apiKey ?? null;
  const googleKey = provider === "google" || provider === null ? apiKey : null;
  const mapboxToken = provider === "mapbox" ? apiKey : null;

  if (vehicle.lastLat == null || vehicle.lastLng == null) {
    return (
      <div className="card flex items-center gap-3 p-4">
        <IconChip tone="neutral">◎</IconChip>
        <span className="text-sm text-muted">{t("vehicle.noPosition")}</span>
      </div>
    );
  }

  const coords = `${vehicle.lastLat},${vehicle.lastLng}`;
  // Mapbox Static Images: lng,lat order, a gold pin over the dark style.
  const mapboxSrc = mapboxToken
    ? `https://api.mapbox.com/styles/v1/mapbox/dark-v11/static/pin-l+FCD704(${vehicle.lastLng},${vehicle.lastLat})/${vehicle.lastLng},${vehicle.lastLat},15/640x224@2x?access_token=${mapboxToken}`
    : null;

  return (
    <div className="space-y-2">
      {googleKey ? (
        <iframe
          title={t("vehicle.position")}
          className="h-56 w-full rounded-card border border-line"
          loading="lazy"
          referrerPolicy="no-referrer-when-downgrade"
          src={`https://www.google.com/maps/embed/v1/place?key=${googleKey}&q=${coords}&zoom=16`}
        />
      ) : mapboxSrc ? (
        // eslint-disable-next-line @next/next/no-img-element
        <img
          alt={t("vehicle.position")}
          className="h-56 w-full rounded-card border border-line object-cover"
          loading="lazy"
          src={mapboxSrc}
        />
      ) : (
        <div className="card p-4">
          <p className="text-sm">{t("vehicle.noMapKey")}</p>
        </div>
      )}
      <div className="flex items-center justify-between">
        <Numeric className="text-xs text-muted">{coords}</Numeric>
        <a
          className="text-xs text-brand underline"
          href={`https://www.google.com/maps/search/?api=1&query=${coords}`}
          target="_blank"
          rel="noreferrer"
        >
          {t("vehicle.openMaps")}
        </a>
      </div>
    </div>
  );
}

/* ------------------------------------------------------------------- control */

function Control({ vehicle, onChanged }: { vehicle: Vehicle; onChanged: () => void }) {
  const { t } = useLocale();
  const [sent, setSent] = useState<VehicleCommand | null>(null);
  const [busy, setBusy] = useState<VehicleCommand | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [statusBusy, setStatusBusy] = useState(false);
  const [statusError, setStatusError] = useState<string | null>(null);

  const send = async (command: VehicleCommand) => {
    setBusy(command);
    setError(null);
    try {
      await api.sendCommand(vehicle.id, command);
      setSent(command);
    } catch (err) {
      setError(err instanceof Error ? err.message : t("common.error"));
    } finally {
      setBusy(null);
    }
  };

  // MAINTENANCE is the one manually-owned status; everything else is derived (telemetry + ride
  // state). So the only status action ops needs is a hold/release toggle.
  const inMaintenance = vehicle.status === "MAINTENANCE";
  const toggleService = async () => {
    setStatusBusy(true);
    setStatusError(null);
    try {
      await api.setStatus(vehicle.id, inMaintenance ? "AVAILABLE" : "MAINTENANCE");
      onChanged();
    } catch (err) {
      setStatusError(err instanceof Error ? err.message : t("common.error"));
    } finally {
      setStatusBusy(false);
    }
  };

  return (
    <div className="space-y-4">
      {/* Service status  works with or without a paired IoT. */}
      <div className="card p-4">
        <div className="flex items-center justify-between gap-3">
          <div>
            <div className="text-sm font-medium">{t("vehicle.serviceStatus")}</div>
            <div className="mt-1.5">
              <StatusBadge tone={inMaintenance ? "warning" : "success"}>
                {vehicle.status}
              </StatusBadge>
            </div>
          </div>
          <Button
            variant={inMaintenance ? "primary" : undefined}
            onClick={toggleService}
            disabled={statusBusy}
          >
            {statusBusy
              ? t("common.loading")
              : inMaintenance
                ? t("vehicle.returnToService")
                : t("vehicle.putInMaintenance")}
          </Button>
        </div>
        {statusError && (
          <div className="tint tone-danger mt-3 rounded-card border p-2 text-xs">{statusError}</div>
        )}
      </div>

      {!vehicle.imei && (
        <div className="card tint tone-warning p-4 text-sm">{t("vehicle.noIot")}</div>
      )}

      {vehicle.imei &&
        COMMAND_GROUPS.map((g) => (
        <div key={g.label}>
          <div className="field-label mb-1.5">{t(g.label as never)}</div>
          <div className="grid grid-cols-3 gap-2">
            {g.items.map((c) => (
              <button
                key={c.value}
                onClick={() => send(c.value)}
                disabled={busy !== null}
                className="card flex flex-col items-center gap-1.5 p-3 text-center transition hover:border-brand disabled:opacity-50"
              >
                <IconChip tone={c.tone}>{c.glyph}</IconChip>
                <span className="text-xs font-medium leading-tight">
                  {busy === c.value ? t("common.loading") : t(`vehicle.cmd${c.value}` as never)}
                </span>
              </button>
            ))}
          </div>
        </div>
      ))}

      {error && (
        <div className="tint tone-danger rounded-card border p-3 text-sm">{error}</div>
      )}

      {sent && !error && (
        <div className="card tint tone-info p-3 text-sm">
          {/* The ack means "received", not "the lock moved", say so plainly. */}
          {t("vehicle.dispatched")}
        </div>
      )}
    </div>
  );
}

/* --------------------------------------------------------------------- owner */

function Owner({
  vehicle,
  investors,
  assignment,
  onChanged,
}: {
  vehicle: Vehicle;
  investors: Investor[];
  assignment: Assignment | null;
  onChanged: () => void;
}) {
  const { t } = useLocale();
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  // A list that works at ten investors is unusable at a thousand: search narrows it, paging keeps
  // the DOM small. The full list is already loaded here, so this filters in memory; move it to a
  // server query when the payload itself becomes the problem.
  const [query, setQuery] = useState("");
  const [page, setPage] = useState(0);

  const needle = query.trim().toLowerCase();
  const matches = needle
    ? investors.filter(
        (i) =>
          i.name.toLowerCase().includes(needle) || i.email.toLowerCase().includes(needle),
      )
    : investors;
  const PER_PAGE = 8;
  const pages = Math.max(1, Math.ceil(matches.length / PER_PAGE));
  const shown = matches.slice(page * PER_PAGE, page * PER_PAGE + PER_PAGE);

  const set = async (investorId: string | null) => {
    setBusy(true);
    setError(null);
    try {
      await api.assignOwner(vehicle.id, investorId);
      onChanged();
    } catch (err) {
      setError(err instanceof Error ? err.message : t("common.error"));
    } finally {
      setBusy(false);
    }
  };

  const ownerId = assignment?.investorId ?? null;

  return (
    <div className="space-y-3">
      <p className="text-sm text-muted">{t("vehicle.ownerHint")}</p>

      <button
        onClick={() => set(null)}
        disabled={busy}
        className={`card flex w-full items-center gap-3 p-4 text-start transition disabled:opacity-50 ${
          ownerId === null ? "border-brand" : "hover:border-line-strong"
        }`}
      >
        <IconChip tone={ownerId === null ? "brand" : "neutral"}>🏢</IconChip>
        <div className="flex-1">
          <div className="text-sm font-medium">{t("vehicle.ownerPlatform")}</div>
          <div className="text-xs text-muted">{t("vehicle.ownerPlatformHint")}</div>
        </div>
        {ownerId === null && <StatusBadge tone="brand">{t("vehicle.current")}</StatusBadge>}
      </button>

      <input
        value={query}
        onChange={(e) => {
          setQuery(e.target.value);
          setPage(0);
        }}
        placeholder={t("vehicle.searchInvestors")}
        className="w-full rounded-field border border-line bg-surface px-3 py-2 text-sm outline-none focus:border-brand"
      />

      {shown.map((inv) => (
        <button
          key={inv.id}
          onClick={() => set(inv.id)}
          disabled={busy}
          className={`card flex w-full items-center gap-3 p-4 text-start transition disabled:opacity-50 ${
            ownerId === inv.id ? "border-brand" : "hover:border-line-strong"
          }`}
        >
          <IconChip tone={ownerId === inv.id ? "brand" : "neutral"}>👤</IconChip>
          <div className="flex-1">
            <div className="text-sm font-medium">{inv.name}</div>
            <div className="text-xs text-muted">{inv.email}</div>
          </div>
          {ownerId === inv.id && <StatusBadge tone="brand">{t("vehicle.current")}</StatusBadge>}
        </button>
      ))}

      {matches.length === 0 && (
        <div className="card p-4 text-sm text-muted">
          {investors.length === 0 ? t("vehicle.noInvestors") : t("vehicle.noMatches")}
        </div>
      )}

      {pages > 1 && (
        <div className="flex items-center justify-between gap-3 pt-1">
          <button
            type="button"
            onClick={() => setPage((p) => Math.max(0, p - 1))}
            disabled={page === 0}
            className="rounded-field border border-line px-3 py-1.5 text-sm disabled:opacity-40"
          >
            {t("common.prev")}
          </button>
          <span className="text-xs text-muted">
            {page + 1} / {pages} &middot; {matches.length}
          </span>
          <button
            type="button"
            onClick={() => setPage((p) => Math.min(pages - 1, p + 1))}
            disabled={page >= pages - 1}
            className="rounded-field border border-line px-3 py-1.5 text-sm disabled:opacity-40"
          >
            {t("common.next")}
          </button>
        </div>
      )}

      {error && (
        <div className="tint tone-danger rounded-card border p-3 text-sm">{error}</div>
      )}
    </div>
  );
}

/* ---------------------------------------------------------------------- logs */

/**
 * One vehicle's history, from the two places it is actually recorded.
 *
 * <p>They answer different questions and neither substitutes for the other: the device frames say
 * what the hardware did, the audit trail says what a person told it to do. A lock that never
 * opened looks identical in the audit log whether the command was sent or not, and only the
 * frames can tell you which.
 */
function Logs({ vehicle }: { vehicle: Vehicle }) {
  const { t } = useLocale();
  const [events, setEvents] = useState<DeviceEvent[] | null>(null);
  const [audit, setAudit] = useState<AuditEntry[] | null>(null);
  // Non-super-admins cannot read the audit trail at all, so its absence is a permission fact
  // rather than an error worth shouting about.
  const [auditDenied, setAuditDenied] = useState(false);
  const [error, setError] = useState(false);
  const [live, setLive] = useState(true);

  const load = useCallback(async () => {
    try {
      setEvents(vehicle.imei ? await api.mqttEvents(vehicle.imei, 50) : []);
      setError(false);
    } catch {
      setError(true);
    }
    try {
      setAudit(await api.auditFor(vehicle.id, 50));
    } catch {
      setAuditDenied(true);
    }
  }, [vehicle.imei, vehicle.id]);

  useEffect(() => {
    load();
    if (!live) return;
    const id = setInterval(load, 5000);
    return () => clearInterval(id);
  }, [load, live]);

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between gap-3">
        <p className="text-sm text-muted">{t("vehicle.logsBody")}</p>
        <Button onClick={() => setLive((v) => !v)}>
          {live ? t("fleetMap.liveOn") : t("fleetMap.liveOff")}
        </Button>
      </div>

      <div>
        <SectionTitle>{t("vehicle.logsDevice")}</SectionTitle>
        {!vehicle.imei ? (
          <p className="text-sm text-muted">{t("vehicle.logsNoIot")}</p>
        ) : error ? (
          <ErrorState onRetry={load} />
        ) : events && events.length === 0 ? (
          <p className="text-sm text-muted">{t("vehicle.logsNoFrames")}</p>
        ) : (
          <Card className="!p-0">
            <ul className="max-h-72 divide-y divide-line overflow-y-auto">
              {(events ?? []).map((e, i) => (
                <li key={`${e.at}-${i}`} className="p-3">
                  <div className="flex items-center gap-2">
                    <StatusBadge tone={e.direction === "IN" ? "info" : "accent"}>
                      {e.direction === "IN" ? "↓" : "↑"} {e.name}
                    </StatusBadge>
                    <span className="text-xs text-muted">
                      <Numeric>{new Date(e.at).toLocaleTimeString()}</Numeric>
                    </span>
                  </div>
                  {/* Raw payload: this is a diagnostic window, and a summarised frame is no use
                      when the question is why the hardware did something unexpected. */}
                  <pre className="mt-1 overflow-x-auto whitespace-pre-wrap break-all text-[11px] text-muted">
                    {e.payload}
                  </pre>
                </li>
              ))}
            </ul>
          </Card>
        )}
      </div>

      <div>
        <SectionTitle>{t("vehicle.logsActions")}</SectionTitle>
        {auditDenied ? (
          <p className="text-sm text-muted">{t("vehicle.logsAuditDenied")}</p>
        ) : audit && audit.length === 0 ? (
          <p className="text-sm text-muted">{t("vehicle.logsNoActions")}</p>
        ) : (
          <Card className="!p-0">
            <ul className="max-h-72 divide-y divide-line overflow-y-auto">
              {(audit ?? []).map((a) => (
                <li key={a.id} className="p-3">
                  <div className="flex flex-wrap items-center justify-between gap-2">
                    <span className="text-sm font-medium">{a.action}</span>
                    <span className="text-xs text-muted">
                      <Numeric>{new Date(a.createdAt).toLocaleString()}</Numeric>
                    </span>
                  </div>
                  <p className="text-xs text-muted">
                    {[a.actorEmail, a.detail].filter(Boolean).join(" · ")}
                  </p>
                </li>
              ))}
            </ul>
          </Card>
        )}
      </div>
    </div>
  );
}

/**
 * Where this vehicle has been, from its own telemetry.
 *
 * Separate from Logs on purpose: Logs mixes device frames with staff actions, which is the right
 * view for "who did what". This one answers a different question - where has the hardware actually
 * been - so it carries only positions, newest first, and says plainly when the device has been
 * quiet.
 */
function Location({ vehicle }: { vehicle: Vehicle }) {
  const { t } = useLocale();
  const [hours, setHours] = useState(24);
  const [rows, setRows] = useState<TelemetrySample[] | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let alive = true;
    setRows(null);
    setError(null);
    api
      .vehicleTelemetry(vehicle.id, hours)
      .then((r) => alive && setRows(r))
      .catch(() => alive && setError(t("vehicle.locationFailed")));
    return () => {
      alive = false;
    };
  }, [vehicle.id, hours, t]);

  return (
    <div className="space-y-3">
      <div className="flex items-center justify-between gap-3">
        <p className="text-sm text-muted">{t("vehicle.locationHint")}</p>
        <select
          value={hours}
          onChange={(e) => setHours(Number(e.target.value))}
          className="rounded-field border border-line bg-surface px-2 py-1 text-sm"
        >
          <option value={6}>6h</option>
          <option value={24}>24h</option>
          <option value={72}>72h</option>
          <option value={168}>7d</option>
        </select>
      </div>

      {error && <div className="tint tone-danger rounded-card border p-3 text-sm">{error}</div>}

      {!rows && !error && <div className="card p-4 text-sm text-muted">{t("common.loading")}</div>}

      {rows && rows.length === 0 && (
        <div className="card p-4 text-sm text-muted">{t("vehicle.locationEmpty")}</div>
      )}

      {rows && rows.length > 0 && (
        <>
          <div className="text-xs text-muted">
            {rows.length} {t("vehicle.locationCount")}
          </div>
          <div className="overflow-hidden rounded-card border border-line">
            <table className="w-full text-sm">
              <thead className="bg-surface text-xs text-muted">
                <tr>
                  <th className="px-3 py-2 text-start">{t("vehicle.locationWhen")}</th>
                  <th className="px-3 py-2 text-start">{t("vehicle.locationWhere")}</th>
                  <th className="px-3 py-2 text-end">%</th>
                </tr>
              </thead>
              <tbody>
                {rows.map((r) => (
                  <tr key={r.recordedAt} className="border-t border-line">
                    <td className="px-3 py-2 tabular-nums">
                      {new Date(r.recordedAt).toLocaleString()}
                    </td>
                    <td className="px-3 py-2 tabular-nums">
                      {r.lat != null && r.lng != null ? (
                        <a
                          href={`https://www.google.com/maps?q=${r.lat},${r.lng}`}
                          target="_blank"
                          rel="noreferrer"
                          className="text-brand hover:underline"
                        >
                          {r.lat.toFixed(5)}, {r.lng.toFixed(5)}
                        </a>
                      ) : (
                        <span className="text-muted">-</span>
                      )}
                    </td>
                    <td className="px-3 py-2 text-end tabular-nums">
                      {r.batteryPercent ?? "-"}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </>
      )}
    </div>
  );
}
