<script setup>
import { ref, onMounted, onBeforeUnmount, computed, watch } from "vue";
import { useRouter } from "vue-router";
import { api } from "../api";
import { prefs } from "../prefs";
import {
  formatDate,
  formatPartialDate,
  formatKm,
  formatLiters,
  formatMoney,
  formatConsumption,
  formatKmPerLiter,
  formatKwh,
  formatConsumptionKwh,
  formatKmPerKwh,
  serviceStatus,
  expiryStatus,
  reminderStatus,
} from "../lib/format.js";
import { SERVICE_PARTS, changedParts, visibleParts } from "../lib/serviceParts.js";
import { CAR_TABS, defaultTabFor } from "../lib/tabs.js";
import { t, tSplit } from "../i18n";
import { askConfirm } from "../lib/confirm.js";
import CarFormModal from "../components/CarFormModal.vue";
import ServiceFormModal from "../components/ServiceFormModal.vue";
import TechnicalCheckFormModal from "../components/TechnicalCheckFormModal.vue";
import PartFormModal from "../components/PartFormModal.vue";
import FuelFormModal from "../components/FuelFormModal.vue";
import ChargingFormModal from "../components/ChargingFormModal.vue";
import MaintenanceFormModal from "../components/MaintenanceFormModal.vue";
import DocumentFormModal from "../components/DocumentFormModal.vue";
import ReminderFormModal from "../components/ReminderFormModal.vue";
import ShareModal from "../components/ShareModal.vue";
import ProviderPanel from "../components/ProviderPanel.vue";
import Modal from "../components/Modal.vue";

const props = defineProps({ id: { type: String, required: true } });
const router = useRouter();

const car = ref(null);
const services = ref([]);
const technicalChecks = ref([]);
const parts = ref([]);
const fuel = ref([]);
const fuelStats = ref(null);
const charging = ref([]);
const chargingStats = ref(null);
const maintenance = ref([]);
const documents = ref([]);
const reminders = ref([]);
const loading = ref(true);
const error = ref("");

// Modal state. `editing*` null => create mode; an object => edit that record.
const showCarEdit = ref(false);
const showService = ref(false);
const editingService = ref(null);
const showTechnicalCheck = ref(false);
const editingTechnicalCheck = ref(null);
const showPart = ref(false);
const editingPart = ref(null);
const showFuel = ref(false);
const editingFuel = ref(null);
const showCharging = ref(false);
const editingCharging = ref(null);
const showMaintenance = ref(false);
const editingMaintenance = ref(null);
const showDocument = ref(false);
const editingDocument = ref(null);
const showReminder = ref(false);
const editingReminder = ref(null);

// Delete-car confirmation (guarded: user must type the car name).
const showDeleteCar = ref(false);
const deleteConfirmText = ref("");
const deletingCar = ref(false);

const showShare = ref(false);

const latest = computed(() => services.value[0] || null);
const status = computed(() => serviceStatus(latest.value, car.value));

// Access gating. Owner can do everything (incl. delete + sharing); a "write"
// sharee can edit the car and its records/parts but not delete/share; "read"
// is view-only. Default to owner while loading so nothing flickers as editable
// for a read-only user (car is null until loaded, so buttons are hidden anyway).
const isOwner = computed(() => car.value?.access === "owner");
const canWrite = computed(() => car.value?.access === "owner" || car.value?.access === "write");
const isReadOnly = computed(() => car.value?.access === "read");

// Which tab the page opens on follows the account's default (Settings ›
// Appearance) and otherwise the front of this car's bar. Empty until the car —
// and with it the arrangement — has loaded.
const activeTab = ref("");

// Connected-service tab. It leads the bar by default — ahead of Information —
// because for a car imported from a manufacturer account that is the live view
// of the car, while everything to its right is the record the user keeps by
// hand. Like every tab it can be dragged elsewhere.
//
// It shows for a linked car (labelled with the service, "MyToyota") and also for
// an unlinked one as long as the user has some account connected, where it offers
// to link this car to a vehicle on it. A user with nothing connected never sees
// the tab at all.
const providers = ref([]);
const providerLabel = computed(() => {
  const linked = providers.value.find((p) => p.id === car.value?.provider);
  return linked?.label || car.value?.provider || t("car.tabs.connected");
});
const showProviderTab = computed(
  () => !!car.value?.provider || providers.value.some((p) => p.connected)
);

// Count of reminders wanting attention, surfaced on the tab so it is visible
// without opening it — the whole point of a reminder.
const dueReminders = computed(
  () => reminders.value.filter((r) => r.status === "overdue" || r.status === "due_soon").length
);

// Computed, not a plain array: t() reads the reactive locale, so the tab labels
// have to re-evaluate when the language changes.
//
// Tabs switched off for this car are dropped here. It is a property of the car,
// so everyone it is shared with sees the same page — an EV with Fuel off has no
// Fuel tab for anybody. Information always stays: it is the car itself, and a
// page with no tabs left would be a dead end. The panels below are keyed off
// activeTab, so a hidden tab's content is unreachable rather than unlabelled.
const hiddenTabs = computed(() => car.value?.hiddenTabs || []);
const hiddenFields = computed(() => car.value?.hiddenFields || []);
// The tabs in their default order (lib/tabs.js, shared with the default-tab
// picker in Settings). Information sits second because the connected service,
// when there is one, is what you came to look at.
const ALL_TAB_KEYS = CAR_TABS;

// The arrangement of the bar: the full list of tab keys, hidden ones and the
// connected-service tab included, so a tab switched back on — or a provider
// linked later — returns to where it was put rather than to the end. Its own
// ref rather than read off the car, because a drag rearranges it live and only
// saves on drop. Rebuilt whenever the car is (re)loaded.
const tabKeys = ref([...ALL_TAB_KEYS]);
watch(
  () => car.value?.tabOrder,
  (order) => {
    const arranged = [];
    for (const key of order || []) {
      if (ALL_TAB_KEYS.includes(key) && !arranged.includes(key)) arranged.push(key);
    }
    // A tab the stored arrangement doesn't mention — one added in a later
    // release — follows the arranged ones, the same rule the Information rows
    // and the garage use.
    tabKeys.value = [...arranged, ...ALL_TAB_KEYS.filter((k) => !arranged.includes(k))];
  },
  { immediate: true }
);

const TABS = computed(() =>
  tabKeys.value
    .filter((key) => !hiddenTabs.value.includes(key))
    .filter((key) => key !== "provider" || showProviderTab.value)
    .map((key) => ({ key, label: tabPickerLabel(key) }))
);

// Landing tab, and the guard that keeps the page off a tab with no button —
// one switched off while standing on it, or a provider tab that doesn't apply to
// the car just opened.
//
// Until the user picks a tab the page keeps following the bar: the car (with its
// arrangement) and the profile (with the default) both load after this view
// mounts, so the first render has nothing to go on yet. A click or a drag ends
// that — the page then stays where they put it.
const tabPicked = ref(false);
watch(
  [TABS, () => prefs.defaultTabs],
  ([tabs]) => {
    const keys = tabs.map((tab) => tab.key);
    if (!keys.length) return;
    if (!tabPicked.value) activeTab.value = defaultTabFor("car", keys);
    else if (!keys.includes(activeTab.value)) activeTab.value = keys[0];
  },
  { immediate: true }
);

function selectTab(key) {
  tabPicked.value = true;
  activeTab.value = key;
}

// Dragging a tab, on the same native drag events as the Information rows and the
// garage — so pointer-only, as touch browsers don't fire these. Needs write
// access, since the arrangement belongs to the car; a bar with one tab left has
// nothing to rearrange; and the rail's lock holds it still for a user who would
// rather not nudge the bar on the way to a tab.
const canArrangeTabs = computed(
  () => canWrite.value && !prefs.dragLocked && TABS.value.length > 1
);
const dragTab = ref(""); // tab being dragged
const dropTab = ref(""); // tab it is currently hovering over
const tabOrderError = ref("");
let tabsMoved = false; // the bar changed during this drag and isn't saved yet

function onTabDragStart(key, e) {
  dragTab.value = key;
  // Rearranging the bar must not pull the content out from under the drag, so
  // the page stops following the arrangement the moment one starts.
  tabPicked.value = true;
  tabsMoved = false;
  e.dataTransfer.effectAllowed = "move";
  // Firefox only starts a drag once something is on the transfer.
  e.dataTransfer.setData("text/plain", key);
}

// Reorder live as the pointer crosses tabs, so the bar shows the arrangement you
// are about to get. dragenter fires again for every child element inside the
// same button — the reminder count badge — so the tab being hovered is
// remembered and only a genuinely new one moves anything. The splice works on
// the full list, hidden tabs included, which keeps a hidden tab anchored between
// the same two visible neighbours.
function onTabDragEnter(key) {
  if (!dragTab.value || key === dragTab.value || dropTab.value === key) return;
  dropTab.value = key;
  const list = tabKeys.value;
  const from = list.indexOf(dragTab.value);
  const to = list.indexOf(key);
  if (from < 0 || to < 0) return;
  list.splice(to, 0, ...list.splice(from, 1));
  tabsMoved = true;
}

// Save whatever the bar now shows. Called from both drop and dragend: a tab
// released over the gap beside the bar never produces a drop, and leaving that
// arrangement unsaved would quietly undo itself on the next load.
async function commitTabOrder() {
  dragTab.value = "";
  dropTab.value = "";
  if (!tabsMoved) return;
  tabsMoved = false;
  tabOrderError.value = "";
  try {
    const updated = await api.updateCarView(props.id, { tabOrder: tabKeys.value });
    car.value = { ...updated, access: car.value.access };
  } catch (e) {
    // The arrangement didn't stick; say so and put the stored one back rather
    // than leaving the page showing an order the server doesn't have.
    tabOrderError.value = e.message;
    await load();
  }
}

// --- What this car's page shows (write access; owner or write-shared) ---
//
// Two hidden sets, both properties of the car: the tabs, and the rows of the
// Information tab. Edited as a draft in one modal and saved together, rather
// than saving on every checkbox: switching several off one at a time would make
// the page rearrange under the pointer between clicks.
const showViewPicker = ref(false);
const HIDEABLE_TABS = [
  "provider", "services", "technical", "maintenance", "fuel", "charging",
  "documents", "parts", "reminders",
];
// The Information rows, in their default order. Keys mirror hideableCarFields
// in the API's cars.go — the server rejects anything else.
const INFO_FIELD_KEYS = [
  "oilSpec", "transmissionOil", "differentialOil", "brakeFluid", "coolant",
  "odometer", "serviceInterval", "nextDue", "registrationPlate",
  "registrationCountry", "vin", "fuelType", "buildDate", "firstRegistration",
];
// The Service history columns, in their default order. Keys mirror
// hideableServiceColumns/arrangeableServiceColumns in the API's cars.go — the
// server rejects anything else. Date is in the list because it can be moved,
// but not in HIDEABLE_SERVICE_COLUMNS: a service is the day it happened, and a
// table of them with the day taken out stops being a history.
const ALL_SERVICE_COLUMN_KEYS = [
  "date", "km", "nextDate", "nextKm", "parts", "notes", "file",
];
const HIDEABLE_SERVICE_COLUMNS = ALL_SERVICE_COLUMN_KEYS.filter((key) => key !== "date");
const tabDraft = ref([]); // tab keys that stay visible
const fieldDraft = ref([]); // Information keys that stay visible
const columnDraft = ref([]); // Service history columns that stay visible
const partDraft = ref([]); // parts the service form keeps offering
const viewSaving = ref(false);
const viewError = ref("");

function openViewPicker() {
  tabDraft.value = HIDEABLE_TABS.filter((key) => !hiddenTabs.value.includes(key));
  fieldDraft.value = fieldKeys.value.filter((key) => !hiddenFields.value.includes(key));
  columnDraft.value = serviceColumnKeys.value.filter(
    (key) => key !== "date" && !hiddenServiceColumns.value.includes(key)
  );
  partDraft.value = visibleParts(car.value).map((part) => part.key);
  viewError.value = "";
  showViewPicker.value = true;
}

// One handler per draft rather than passing the ref in from the template: Vue
// unwraps refs in the render context, so a shared handler would be handed the
// plain array and its writes would go nowhere.
function toggleTabDraft(key, on) {
  tabDraft.value = on ? [...tabDraft.value, key] : tabDraft.value.filter((k) => k !== key);
}
function toggleFieldDraft(key, on) {
  fieldDraft.value = on ? [...fieldDraft.value, key] : fieldDraft.value.filter((k) => k !== key);
}
function toggleColumnDraft(key, on) {
  columnDraft.value = on ? [...columnDraft.value, key] : columnDraft.value.filter((k) => k !== key);
}
function togglePartDraft(key, on) {
  partDraft.value = on ? [...partDraft.value, key] : partDraft.value.filter((k) => k !== key);
}

async function saveView() {
  viewSaving.value = true;
  viewError.value = "";
  try {
    const updated = await api.updateCarView(props.id, {
      hiddenTabs: HIDEABLE_TABS.filter((key) => !tabDraft.value.includes(key)),
      hiddenFields: INFO_FIELD_KEYS.filter((key) => !fieldDraft.value.includes(key)),
      hiddenServiceColumns: HIDEABLE_SERVICE_COLUMNS.filter((key) => !columnDraft.value.includes(key)),
      hiddenServiceParts: SERVICE_PARTS.filter((part) => !partDraft.value.includes(part.key)).map((part) => part.key),
    });
    car.value = { ...updated, access: car.value.access };
    showViewPicker.value = false;
  } catch (e) {
    viewError.value = e.message;
  } finally {
    viewSaving.value = false;
  }
}

// The connected-service tab is only offered when this car could show one at all
// — hiding a tab nobody can see would just be confusing.
const tabPickerKeys = computed(() =>
  HIDEABLE_TABS.filter((key) => key !== "provider" || showProviderTab.value)
);
function tabPickerLabel(key) {
  return key === "provider" ? providerLabel.value : t(`car.tabs.${key}`);
}
function infoFieldLabel(key) {
  return t(`car.info.${key}`);
}
// The column headings were translated as car.services.col* long before they
// became keys, so the two are mapped rather than derived: renaming a dozen
// strings in three languages to save this table would be the wrong trade. The
// parts column borrows the form's heading on purpose — the column and the
// dialog's section are the same thing, and they should not drift into wording
// it differently.
const SERVICE_COLUMN_LABELS = {
  date: "car.services.colDate",
  km: "car.services.colKm",
  nextDate: "car.services.colNextDate",
  nextKm: "car.services.colNextKm",
  parts: "forms.service.changedParts",
  notes: "car.services.colNotes",
  file: "car.services.colFile",
};
function serviceColumnLabel(key) {
  return t(SERVICE_COLUMN_LABELS[key]);
}

// --- The arrangement of the Information rows ---
//
// The full order of all 14 keys, hidden ones included, so a row switched back on
// returns to where it was rather than to the end. Kept as its own ref rather
// than read off the car, because a drag rearranges it live and only saves on
// drop. Rebuilt whenever the car is (re)loaded.
const fieldKeys = ref([...INFO_FIELD_KEYS]);
watch(
  () => car.value?.fieldOrder,
  (order) => {
    const arranged = [];
    for (const key of order || []) {
      if (INFO_FIELD_KEYS.includes(key) && !arranged.includes(key)) arranged.push(key);
    }
    // Anything the stored arrangement doesn't mention — a row added in a later
    // release — follows the arranged ones, so it shows up at the end rather than
    // in the middle of somebody's layout. Matches what the garage does.
    fieldKeys.value = [...arranged, ...INFO_FIELD_KEYS.filter((k) => !arranged.includes(k))];
  },
  { immediate: true }
);

// Dragging a row, on the same native drag events as the garage — hand-rolled
// rather than a drag library, which does mean it is pointer-only, as touch
// browsers don't fire these. Needs write access, and there is nothing to
// rearrange with a single row showing.
const canArrangeFields = computed(
  () => canWrite.value && !prefs.dragLocked && infoFields.value.length > 1
);
const dragField = ref(""); // row being dragged
const dropField = ref(""); // row it is currently hovering over
const fieldOrderError = ref("");
let fieldsMoved = false; // the grid changed during this drag and isn't saved yet

function onFieldDragStart(key, e) {
  dragField.value = key;
  fieldsMoved = false;
  e.dataTransfer.effectAllowed = "move";
  // Firefox only starts a drag once something is on the transfer.
  e.dataTransfer.setData("text/plain", key);
}

// Reorder live as the pointer crosses rows, so the grid shows the arrangement
// you are about to get. dragenter fires again for every child element inside the
// same row, so the row being hovered is remembered and only a genuinely new one
// moves anything. The splice works on the full list, hidden rows included, which
// keeps a hidden row anchored between the same two visible neighbours.
function onFieldDragEnter(key) {
  if (!dragField.value || key === dragField.value || dropField.value === key) return;
  dropField.value = key;
  const list = fieldKeys.value;
  const from = list.indexOf(dragField.value);
  const to = list.indexOf(key);
  if (from < 0 || to < 0) return;
  list.splice(to, 0, ...list.splice(from, 1));
  fieldsMoved = true;
}

// Save whatever the grid now shows. Called from both drop and dragend: a row
// released over the gap between rows never produces a drop, and leaving that
// arrangement unsaved would quietly undo itself on the next load.
async function commitFieldOrder() {
  dragField.value = "";
  dropField.value = "";
  if (!fieldsMoved) return;
  fieldsMoved = false;
  fieldOrderError.value = "";
  try {
    const updated = await api.updateCarView(props.id, { fieldOrder: fieldKeys.value });
    car.value = { ...updated, access: car.value.access };
  } catch (e) {
    // The arrangement didn't stick; say so and put the stored one back rather
    // than leaving the page showing an order the server doesn't have.
    fieldOrderError.value = e.message;
    await load();
  }
}

// The Information rows as data, so the same list drives both the grid and the
// picker and the two can't drift apart. `mono` marks the values that read as
// figures rather than prose.
const infoFields = computed(() => {
  const c = car.value;
  if (!c) return [];
  const values = {
    oilSpec: { text: c.oilSpec || t("common.empty") },
    transmissionOil: { text: c.transmissionOilSpec || t("common.empty") },
    differentialOil: { text: c.differentialOilSpec || t("common.empty") },
    brakeFluid: { text: c.brakeFluidSpec || t("common.empty") },
    coolant: { text: c.coolantSpec || t("common.empty") },
    odometer: { text: formatKm(c.currentKm), mono: true },
    serviceInterval: { text: `${c.serviceIntervalDays}d · ${formatKm(c.serviceIntervalKm)}`, mono: true },
    nextDue: {
      text: `${formatDate(latest.value?.nextServiceDate)} · ${formatKm(latest.value?.nextServiceKm)}`,
      mono: true,
    },
    registrationPlate: { text: c.registration || t("common.empty"), mono: true },
    registrationCountry: { text: c.registrationCountry || t("common.empty") },
    vin: { text: c.vin || t("common.empty"), mono: true },
    fuelType: { text: fuelLabel(c.fuelType) },
    // A build date may be a year or a month rather than a day — printed to
    // whatever precision it was given, not padded out to a day nobody knew.
    buildDate: { text: c.buildDate ? formatPartialDate(c.buildDate) : t("common.empty"), mono: true },
    firstRegistration: {
      text: c.firstRegistrationDate ? formatDate(c.firstRegistrationDate) : t("common.empty"),
      mono: true,
    },
  };
  return fieldKeys.value.filter((key) => !hiddenFields.value.includes(key)).map((key) => ({
    key,
    label: infoFieldLabel(key),
    ...values[key],
  }));
});

// --- The Service history columns ---
//
// The same three pieces as the Information rows, on the same reasoning: a
// hidden set so a column added in a later release is on by default, an
// arrangement that covers the hidden columns so one switched back on returns to
// where it was, and both properties of the car, so everyone it is shared with
// sees the same table.
const hiddenServiceColumns = computed(() => car.value?.hiddenServiceColumns || []);
const serviceColumnKeys = ref([...ALL_SERVICE_COLUMN_KEYS]);
watch(
  () => car.value?.serviceColumnOrder,
  (order) => {
    const arranged = [];
    for (const key of order || []) {
      if (ALL_SERVICE_COLUMN_KEYS.includes(key) && !arranged.includes(key)) arranged.push(key);
    }
    // A column the stored arrangement doesn't mention follows the arranged ones,
    // so it joins at the right-hand end rather than in the middle of somebody's
    // table — the rule the tabs, the Information rows and the garage all use.
    serviceColumnKeys.value = [
      ...arranged,
      ...ALL_SERVICE_COLUMN_KEYS.filter((k) => !arranged.includes(k)),
    ];
  },
  { immediate: true }
);

// The visible columns in their arranged order, as data, so the head and the body
// are driven by one list and cannot drift apart when a column moves or goes
// away.
const serviceColumns = computed(() =>
  serviceColumnKeys.value
    .filter((key) => !hiddenServiceColumns.value.includes(key))
    .map((key) => ({ key, label: serviceColumnLabel(key) }))
);

// What a cell with nothing in it looks like, in one place. An em dash is not
// content: it reads muted, and at the row's own size rather than at the size of
// whatever button would have stood there instead. The three cells that can be
// empty each used to do this their own way — a body-coloured dash under Notes,
// a smaller one under File, a muted one under Changed parts — so one row showed
// the same "nothing" three different ways.
const EMPTY_CELL = "text-muted";

// One cell of that table. Returns the text and the classes it carries beyond the
// shared padding; the file column is the one whose cell is a button, and says so
// rather than returning text the template would have to special-case by key.
function serviceCell(s, key) {
  switch (key) {
    case "date":
      return { text: formatDate(s.date), classes: "whitespace-nowrap data font-medium text-strong" };
    case "km":
      return { text: formatKm(s.km), classes: "whitespace-nowrap data text-body" };
    case "nextDate":
      return { text: formatDate(s.nextServiceDate), classes: "whitespace-nowrap data text-muted" };
    case "nextKm":
      return { text: formatKm(s.nextServiceKm), classes: "whitespace-nowrap data text-muted" };
    case "parts":
      return { parts: partsSummary(s), classes: "whitespace-nowrap" };
    case "notes":
      return { text: s.notes || t("common.empty"), classes: s.notes ? "text-body" : EMPTY_CELL };
    default: // file
      return { file: true, classes: "whitespace-nowrap" };
  }
}

// What the Changed parts cell says before it is opened. Naming the parts beats a
// bare count — the point of a history is to be read down the page — but the list
// has to stay one line wide, and it is going to grow, so past two it becomes the
// first part and a tally. The panel behind it has the full picture either way.
function partsSummary(s) {
  const changed = changedParts(s, car.value);
  if (!changed.length) return { text: t("common.empty"), muted: true, count: 0 };
  const labels = changed.map((part) => t(part.label));
  return {
    text: labels.length > 2 ? `${labels[0]} +${labels.length - 1}` : labels.join(", "),
    muted: false,
    count: changed.length,
  };
}

// One row's cells, already in the arranged order. A row at a time rather than a
// call per cell, so a table of twenty services doesn't rebuild every cell three
// times over to read its classes, its text and whether it is the file column.
function serviceRow(s) {
  return serviceColumns.value.map((col) => ({ ...col, ...serviceCell(s, col.key) }));
}

// --- The Changed parts panel ---
//
// A dropdown rather than a dialog. This is read-only detail about one row of a
// table meant to be read down the page: a modal would black out the rows you
// are comparing against and cost an open-and-close for every one of them. It is
// positioned fixed from the button's own rectangle because the table scrolls
// sideways inside a clipping box, which an absolutely positioned panel could
// not get out of.
const openParts = ref(""); // id of the record whose panel is open
const partsAt = ref({ left: 0, top: 0 });
const PARTS_PANEL_WIDTH = 240; // keep in step with the panel's w-60 below

// One panel, not one per row: it is fixed to a point on the screen and only ever
// shows a single record, so the row it belongs to is a lookup rather than a
// panel sitting in all of them.
const openPartsRecord = computed(
  () => services.value.find((s) => s.id === openParts.value) || null
);

function togglePartsPanel(id, e) {
  if (openParts.value === id) return closePartsPanel();
  const rect = e.currentTarget.getBoundingClientRect();
  // Kept on screen: opened under the right-hand columns it would otherwise hang
  // off the edge of the window.
  partsAt.value = {
    left: Math.max(8, Math.min(rect.left, window.innerWidth - PARTS_PANEL_WIDTH - 8)),
    top: rect.bottom + 6,
  };
  openParts.value = id;
}
function closePartsPanel() {
  openParts.value = "";
}
function onPartsKey(e) {
  if (e.key === "Escape") closePartsPanel();
}

// A click anywhere else closes it — the opening click stops before it gets here
// — and so does Escape. Scrolling closes it too: the panel is pinned to a point
// on the screen, so a page or a table that moves underneath it would leave it
// pointing at the wrong row.
onMounted(() => {
  document.addEventListener("click", closePartsPanel);
  document.addEventListener("keydown", onPartsKey);
  window.addEventListener("scroll", closePartsPanel, true);
});
onBeforeUnmount(() => {
  document.removeEventListener("click", closePartsPanel);
  document.removeEventListener("keydown", onPartsKey);
  window.removeEventListener("scroll", closePartsPanel, true);
});

// Dragging a column header, on the same native drag events as the tabs, the
// Information rows and the garage — so pointer-only, as touch browsers don't
// fire these. Needs write access, since the arrangement belongs to the car, and
// there is nothing to rearrange with one column showing.
const canArrangeServiceColumns = computed(
  () => canWrite.value && !prefs.dragLocked && serviceColumns.value.length > 1
);
const dragColumn = ref(""); // column being dragged
const dropColumn = ref(""); // column it is currently hovering over
const columnOrderError = ref("");
let columnsMoved = false; // the table changed during this drag and isn't saved yet

function onColumnDragStart(key, e) {
  dragColumn.value = key;
  columnsMoved = false;
  e.dataTransfer.effectAllowed = "move";
  // Firefox only starts a drag once something is on the transfer.
  e.dataTransfer.setData("text/plain", key);
}

// Reorder live as the pointer crosses headers, so the table shows the
// arrangement you are about to get. The splice works on the full list, hidden
// columns included, which keeps a hidden column anchored between the same two
// visible neighbours.
function onColumnDragEnter(key) {
  if (!dragColumn.value || key === dragColumn.value || dropColumn.value === key) return;
  dropColumn.value = key;
  const list = serviceColumnKeys.value;
  const from = list.indexOf(dragColumn.value);
  const to = list.indexOf(key);
  if (from < 0 || to < 0) return;
  list.splice(to, 0, ...list.splice(from, 1));
  columnsMoved = true;
}

// Save whatever the table now shows. Called from both drop and dragend: a header
// released past the end of the row never produces a drop, and leaving that
// arrangement unsaved would quietly undo itself on the next load.
async function commitServiceColumnOrder() {
  dragColumn.value = "";
  dropColumn.value = "";
  if (!columnsMoved) return;
  columnsMoved = false;
  columnOrderError.value = "";
  try {
    const updated = await api.updateCarView(props.id, { serviceColumnOrder: serviceColumnKeys.value });
    car.value = { ...updated, access: car.value.access };
  } catch (e) {
    // The arrangement didn't stick; say so and put the stored one back rather
    // than leaving the page showing an order the server doesn't have.
    columnOrderError.value = e.message;
    await load();
  }
}

async function load() {
  loading.value = true;
  error.value = "";
  try {
    [
      car.value,
      services.value,
      technicalChecks.value,
      parts.value,
      fuel.value,
      fuelStats.value,
      charging.value,
      chargingStats.value,
      maintenance.value,
      documents.value,
      reminders.value,
    ] = await Promise.all([
      api.getCar(props.id),
      api.listCarServices(props.id),
      api.listCarTechnicalChecks(props.id),
      api.listCarParts(props.id),
      api.listCarFuel(props.id),
      api.getCarFuelStats(props.id),
      api.listCarCharging(props.id),
      api.getCarChargingStats(props.id),
      api.listCarMaintenance(props.id),
      api.listCarDocuments(props.id),
      api.listCarReminders(props.id),
    ]);
  } catch (e) {
    error.value = e.message;
  } finally {
    loading.value = false;
  }

  // Which manufacturer services the user has connected — it decides whether the
  // connected-service tab appears. Fired separately and failure-tolerant: a
  // plugin being down must not take the car page with it.
  api
    .listVehicleProviders()
    .then((list) => (providers.value = list))
    .catch(() => {});
}

function openAddService() {
  editingService.value = null;
  showService.value = true;
}
function openEditService(s) {
  editingService.value = s;
  showService.value = true;
}
async function onServiceSaved() {
  showService.value = false;
  editingService.value = null;
  // A service record moves the next-service-due reminder, so reload everything.
  await load();
}
async function deleteService(id) {
  if (!(await askConfirm(t("car.services.confirmDelete")))) return;
  try {
    await api.deleteService(id);
    await load();
  } catch (e) {
    error.value = e.message;
  }
}

// --- technical checks ---
function openAddTechnicalCheck() {
  editingTechnicalCheck.value = null;
  showTechnicalCheck.value = true;
}
function openEditTechnicalCheck(t) {
  editingTechnicalCheck.value = t;
  showTechnicalCheck.value = true;
}
async function onTechnicalCheckSaved() {
  showTechnicalCheck.value = false;
  editingTechnicalCheck.value = null;
  await load();
}
async function deleteTechnicalCheck(id) {
  if (!(await askConfirm(t("car.technical.confirmDelete")))) return;
  try {
    await api.deleteTechnicalCheck(id);
    await load();
  } catch (e) {
    error.value = e.message;
  }
}

function openAddPart() {
  editingPart.value = null;
  showPart.value = true;
}
function openEditPart(p) {
  editingPart.value = p;
  showPart.value = true;
}
async function onPartSaved() {
  showPart.value = false;
  editingPart.value = null;
  parts.value = await api.listCarParts(props.id);
}
async function deletePart(id) {
  if (!(await askConfirm(t("car.parts.confirmDelete")))) return;
  try {
    await api.deletePart(id);
    parts.value = await api.listCarParts(props.id);
  } catch (e) {
    error.value = e.message;
  }
}

// --- fuel ---
function openAddFuel() {
  editingFuel.value = null;
  showFuel.value = true;
}
function openEditFuel(f) {
  editingFuel.value = f;
  showFuel.value = true;
}
async function reloadFuel() {
  // Editing one refill re-derives every window it touches, and a refill also
  // advances the car's odometer — which in turn moves any km-triggered reminder.
  // So refetch all four, not just the list that was edited.
  [fuel.value, fuelStats.value, car.value, reminders.value] = await Promise.all([
    api.listCarFuel(props.id),
    api.getCarFuelStats(props.id),
    api.getCar(props.id),
    api.listCarReminders(props.id),
  ]);
}
async function onFuelSaved() {
  showFuel.value = false;
  editingFuel.value = null;
  await reloadFuel();
}
async function deleteFuel(id) {
  if (!(await askConfirm(t("car.fuel.confirmDelete")))) return;
  try {
    await api.deleteFuel(id);
    await reloadFuel();
  } catch (e) {
    error.value = e.message;
  }
}

// --- charging ---
//
// The same handling as fuel, for the same reasons: a session re-derives every
// window it touches and advances the car's odometer, so the whole set is
// refetched rather than the one list that was edited.
function openAddCharging() {
  editingCharging.value = null;
  showCharging.value = true;
}
function openEditCharging(c) {
  editingCharging.value = c;
  showCharging.value = true;
}
async function reloadCharging() {
  [charging.value, chargingStats.value, car.value, reminders.value] = await Promise.all([
    api.listCarCharging(props.id),
    api.getCarChargingStats(props.id),
    api.getCar(props.id),
    api.listCarReminders(props.id),
  ]);
}
async function onChargingSaved() {
  showCharging.value = false;
  editingCharging.value = null;
  await reloadCharging();
}
async function deleteCharging(id) {
  if (!(await askConfirm(t("car.charging.confirmDelete")))) return;
  try {
    await api.deleteCharging(id);
    await reloadCharging();
  } catch (e) {
    error.value = e.message;
  }
}

// --- maintenance ---
function openAddMaintenance() {
  editingMaintenance.value = null;
  showMaintenance.value = true;
}
function openEditMaintenance(m) {
  editingMaintenance.value = m;
  showMaintenance.value = true;
}
async function onMaintenanceSaved() {
  showMaintenance.value = false;
  editingMaintenance.value = null;
  // A completed visit advances the odometer, which moves km-triggered reminders.
  [maintenance.value, car.value, reminders.value] = await Promise.all([
    api.listCarMaintenance(props.id),
    api.getCar(props.id),
    api.listCarReminders(props.id),
  ]);
}
async function deleteMaintenance(id) {
  if (!(await askConfirm(t("car.maintenance.confirmDelete")))) return;
  try {
    await api.deleteMaintenance(id);
    maintenance.value = await api.listCarMaintenance(props.id);
  } catch (e) {
    error.value = e.message;
  }
}

// --- documents ---
function openAddDocument() {
  editingDocument.value = null;
  showDocument.value = true;
}
function openEditDocument(d) {
  editingDocument.value = d;
  showDocument.value = true;
}
async function onDocumentSaved() {
  showDocument.value = false;
  editingDocument.value = null;
  // A renewal date change adds/moves an auto-derived reminder.
  [documents.value, reminders.value] = await Promise.all([
    api.listCarDocuments(props.id),
    api.listCarReminders(props.id),
  ]);
}
async function deleteDocument(id) {
  if (!(await askConfirm(t("car.documents.confirmDelete")))) return;
  try {
    await api.deleteDocument(id);
    [documents.value, reminders.value] = await Promise.all([
      api.listCarDocuments(props.id),
      api.listCarReminders(props.id),
    ]);
  } catch (e) {
    error.value = e.message;
  }
}
// --- attachments ---
// The file needs the auth header, so it is fetched as a Blob rather than linked
// to directly. `kind` names one of api.files — the same keys as the tabs.
async function downloadAttachment(kind, record) {
  try {
    const { blob, filename } = await api.files[kind].download(record.id);
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = filename || record.fileName || "attachment";
    a.click();
    URL.revokeObjectURL(url);
  } catch (e) {
    error.value = e.message;
  }
}

// --- reminders ---
function openAddReminder() {
  editingReminder.value = null;
  showReminder.value = true;
}
function openEditReminder(r) {
  editingReminder.value = r;
  showReminder.value = true;
}
async function onReminderSaved() {
  showReminder.value = false;
  editingReminder.value = null;
  reminders.value = await api.listCarReminders(props.id);
}
async function completeReminder(r) {
  try {
    await api.completeReminder(r.id);
    reminders.value = await api.listCarReminders(props.id);
  } catch (e) {
    error.value = e.message;
  }
}
async function reopenReminder(r) {
  try {
    await api.updateReminder(r.id, {
      car: r.car,
      title: r.title,
      type: r.type,
      dueDate: r.dueDate ?? null,
      dueKm: r.dueKm || 0,
      repeatDays: r.repeatDays || 0,
      repeatKm: r.repeatKm || 0,
      done: false,
      notes: r.notes || "",
    });
    reminders.value = await api.listCarReminders(props.id);
  } catch (e) {
    error.value = e.message;
  }
}
async function deleteReminder(id) {
  if (!(await askConfirm(t("car.reminders.confirmDelete")))) return;
  try {
    await api.deleteReminder(id);
    reminders.value = await api.listCarReminders(props.id);
  } catch (e) {
    error.value = e.message;
  }
}

async function onCarSaved(updated) {
  showCarEdit.value = false;
  car.value = updated;
}

// The provider panel writes to the car too — it links/unlinks the connected
// service and can take the odometer from it, which moves km-triggered reminders.
async function onCarUpdated(updated) {
  car.value = updated;
  reminders.value = await api.listCarReminders(props.id);
}

function openDeleteCar() {
  deleteConfirmText.value = "";
  showDeleteCar.value = true;
}

// Enabled only once the typed name matches — guards this cascade delete.
const canDeleteCar = computed(
  () => car.value && deleteConfirmText.value.trim() === car.value.name
);

async function confirmDeleteCar() {
  if (!canDeleteCar.value) return;
  deletingCar.value = true;
  error.value = "";
  try {
    await api.deleteCar(props.id);
    router.push({ name: "dashboard" });
  } catch (e) {
    error.value = e.message;
    deletingCar.value = false;
  }
}

function yn(v) {
  return v ? t("common.yes") : t("common.no");
}

// Enum → localized label. Each of these mirrors a block in enums.* of the
// language files; an unknown value falls through to the raw code (or an em dash
// for fuel, which is the "none set" case) rather than a missing-key marker.
function fuelLabel(v) {
  return v ? t(`enums.fuelType.${v}`) : t("common.empty");
}
function maintenanceTypeLabel(v) {
  return v ? t(`enums.maintenanceType.${v}`) : v;
}
function maintenanceStatusLabel(v) {
  return v ? t(`enums.maintenanceStatus.${v}`) : v;
}
function documentTypeLabel(v) {
  return v ? t(`enums.documentType.${v}`) : v;
}
function reminderTypeLabel(v) {
  return v ? t(`enums.reminderTypeShort.${v}`) : v;
}

const MAINTENANCE_STATUS = {
  scheduled: "dh-badge dh-badge-warning",
  in_progress: "dh-badge dh-badge-warning",
  completed: "dh-badge dh-badge-success",
};

onMounted(load);
</script>

<template>
  <div>
    <RouterLink to="/" class="mb-4 inline-flex items-center gap-1 text-sm font-medium text-brandtext hover:underline">
      {{ t("car.allCars") }}
    </RouterLink>

    <p v-if="error" class="mb-4 rounded-control bg-danger-soft px-4 py-3 text-sm font-medium text-danger">{{ error }}</p>
    <p v-if="loading" class="text-muted">{{ t("common.loading") }}</p>

    <template v-else-if="car">
      <!-- Header -->
      <div class="dh-card mb-6 p-6">
        <div class="flex flex-wrap items-start justify-between gap-3">
          <div>
            <h1 class="text-2xl font-bold tracking-[-0.03em] text-strong">{{ car.name }}</h1>
            <p class="text-sm text-muted">
              {{ [car.make, car.model, car.year || ''].filter(Boolean).join(' ') }}
              <span v-if="car.registration"> · {{ car.registration }}</span>
            </p>
          </div>
          <div class="flex flex-wrap items-center gap-2">
            <span v-if="!isOwner" class="dh-badge dh-badge-neutral">
              {{ isReadOnly ? t("car.sharedReadOnly") : t("car.shared") }}
            </span>
            <span :class="status.classes">{{ status.label }}</span>
            <!-- Which tabs this car's page shows. -->
            <button
              v-if="canWrite"
              class="dh-btn dh-btn-ghost !px-2 !py-1.5"
              :title="t('car.viewPicker.open')"
              :aria-label="t('car.viewPicker.open')"
              @click="openViewPicker"
            >
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M9.6 3.6 9 6a7.5 7.5 0 0 0-1.7 1L5 6.3l-2 3.4 2 1.5a7.6 7.6 0 0 0 0 2l-2 1.5 2 3.4 2.3-.7c.5.4 1.1.8 1.7 1l.6 2.4h4l.6-2.4c.6-.2 1.2-.6 1.7-1l2.3.7 2-3.4-2-1.5a7.6 7.6 0 0 0 0-2l2-1.5-2-3.4-2.3.7A7.5 7.5 0 0 0 15 6l-.6-2.4z"/><circle cx="12" cy="12" r="2.6"/></svg>
            </button>
            <button v-if="isOwner" class="dh-btn dh-btn-ghost !px-3 !py-1.5" @click="showShare = true">{{ t("car.share") }}</button>
            <button v-if="canWrite" class="dh-btn dh-btn-ghost !px-3 !py-1.5" @click="showCarEdit = true">{{ t("common.edit") }}</button>
            <button v-if="isOwner" class="dh-btn !px-3 !py-1.5 border border-danger/30 text-danger hover:bg-danger-soft" @click="openDeleteCar">{{ t("common.delete") }}</button>
          </div>
        </div>
      </div>

      <!-- Tabs. They can be dragged into any order with write access; the
           arrangement belongs to the car, like which tabs show at all. -->
      <p v-if="tabOrderError" class="mb-4 rounded-control bg-danger-soft px-4 py-3 text-sm font-medium text-danger">{{ tabOrderError }}</p>
      <div class="mb-6 flex flex-wrap gap-1 border-b border-subtle">
        <button
          v-for="tab in TABS"
          :key="tab.key"
          :draggable="canArrangeTabs"
          :title="canArrangeTabs ? t('car.tabs.dragHint') : ''"
          class="-mb-px flex items-center gap-1.5 border-b-2 px-4 py-2.5 text-sm font-semibold transition-colors"
          :class="[
            activeTab === tab.key
              ? 'border-accent text-brandtext'
              : 'border-transparent text-muted hover:text-strong',
            canArrangeTabs ? 'cursor-grab active:cursor-grabbing' : '',
            dragTab === tab.key ? 'opacity-50' : '',
            dropTab === tab.key ? 'bg-sunken rounded-t-control' : '',
          ]"
          @click="selectTab(tab.key)"
          @dragstart="onTabDragStart(tab.key, $event)"
          @dragenter.prevent="onTabDragEnter(tab.key)"
          @dragover.prevent
          @drop.prevent="commitTabOrder"
          @dragend="commitTabOrder">
          {{ tab.label }}
          <span
            v-if="tab.key === 'reminders' && dueReminders"
            class="rounded-full bg-danger px-1.5 py-0.5 text-[10px] font-bold leading-none text-white">
            {{ dueReminders }}
          </span>
        </button>
      </div>

      <!-- Connected service (MyToyota, …). Mounted only when its tab is open, so
           opening a car never triggers a login against the manufacturer. -->
      <ProviderPanel
        v-if="activeTab === 'provider'"
        :car="car"
        :can-write="canWrite"
        :provider-label="car.provider ? providerLabel : ''"
        @car-updated="onCarUpdated"
      />

      <!-- Information. The rows can be dragged into any order with write access;
           the arrangement belongs to the car, like which rows show at all. -->
      <section v-else-if="activeTab === 'info'">
        <p v-if="fieldOrderError" class="mb-4 rounded-control bg-danger-soft px-4 py-3 text-sm font-medium text-danger">{{ fieldOrderError }}</p>
        <div class="dh-card p-6">
          <p v-if="infoFields.length === 0" class="text-sm text-muted">{{ t("car.info.allHidden") }}</p>
          <dl v-else class="grid grid-cols-2 gap-4 text-sm sm:grid-cols-4">
            <div
              v-for="f in infoFields"
              :key="f.key"
              :draggable="canArrangeFields"
              :title="canArrangeFields ? t('car.info.dragHint') : ''"
              class="rounded-control p-2 -m-2 transition-shadow duration-150"
              :class="[
                canArrangeFields ? 'cursor-grab hover:bg-sunken active:cursor-grabbing' : '',
                dragField === f.key ? 'opacity-50' : '',
                dropField === f.key ? 'ring-2 ring-accent' : '',
              ]"
              @dragstart="onFieldDragStart(f.key, $event)"
              @dragenter.prevent="onFieldDragEnter(f.key)"
              @dragover.prevent
              @drop.prevent="commitFieldOrder"
              @dragend="commitFieldOrder"
            >
              <dt class="eyebrow">{{ f.label }}</dt>
              <dd class="mt-0.5 font-medium text-strong" :class="f.mono ? 'data' : ''">{{ f.text }}</dd>
            </div>
          </dl>
        </div>
        <p v-if="canArrangeFields" class="mt-2 text-xs text-muted">{{ t("car.info.dragHint") }}</p>
      </section>

      <!-- Service history. The columns can be switched off in the view picker and
           dragged into any order with write access; both belong to the car, like
           which tabs and Information rows it shows. -->
      <section v-else-if="activeTab === 'services'">
        <p v-if="columnOrderError" class="mb-4 rounded-control bg-danger-soft px-4 py-3 text-sm font-medium text-danger">{{ columnOrderError }}</p>
        <div class="mb-3 flex items-center justify-between">
          <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.services.title") }}</h2>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddService">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.services.add") }}
          </button>
        </div>

        <div v-if="services.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.services.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th
                  v-for="col in serviceColumns"
                  :key="col.key"
                  :draggable="canArrangeServiceColumns"
                  :title="canArrangeServiceColumns ? t('car.services.dragHint') : ''"
                  :class="[
                    canArrangeServiceColumns ? 'cursor-grab active:cursor-grabbing' : '',
                    dragColumn === col.key ? 'opacity-50' : '',
                    dropColumn === col.key ? 'ring-2 ring-inset ring-accent' : '',
                  ]"
                  @dragstart="onColumnDragStart(col.key, $event)"
                  @dragenter.prevent="onColumnDragEnter(col.key)"
                  @dragover.prevent
                  @drop.prevent="commitServiceColumnOrder"
                  @dragend="commitServiceColumnOrder"
                >{{ col.label }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="s in services" :key="s.id" class="transition-colors hover:bg-sunken">
                <td
                  v-for="cell in serviceRow(s)"
                  :key="cell.key"
                  class="px-4 py-3"
                  :class="cell.classes"
                >
                  <template v-if="cell.file">
                    <button v-if="s.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('services', s)">
                      {{ t("common.download") }}
                    </button>
                    <span v-else :class="EMPTY_CELL">{{ t("common.empty") }}</span>
                  </template>
                  <template v-else-if="cell.parts">
                    <button
                      class="inline-flex items-center gap-1.5 text-left hover:underline"
                      :class="cell.parts.muted ? EMPTY_CELL : 'text-body'"
                      :aria-expanded="openParts === s.id"
                      @click.stop="togglePartsPanel(s.id, $event)"
                    >
                      {{ cell.parts.text }}
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="h-3.5 w-3.5 shrink-0 text-muted"><path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" /></svg>
                    </button>
                  </template>
                  <template v-else>{{ cell.text }}</template>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditService(s)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deleteService(s.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <!-- The Changed parts panel. Rendered here rather than inside the cell so
             one panel serves every row, and pinned to the button it was opened
             from. -->
        <div
          v-if="openPartsRecord"
          class="dh-card fixed z-30 w-60 p-3 shadow-pop"
          :style="{ left: partsAt.left + 'px', top: partsAt.top + 'px' }"
          @click.stop
        >
          <p class="eyebrow mb-2">{{ t("forms.service.changedParts") }}</p>
          <div
            v-for="part in visibleParts(car)"
            :key="part.key"
            class="flex items-center justify-between gap-4 py-0.5 text-sm"
          >
            <span class="text-body">{{ t(part.label) }}</span>
            <span
              class="text-xs font-semibold"
              :class="openPartsRecord[part.field] ? 'text-success' : 'text-muted'"
            >{{ yn(!!openPartsRecord[part.field]) }}</span>
          </div>
        </div>

        <p v-if="canArrangeServiceColumns" class="mt-2 text-xs text-muted">{{ t("car.services.dragHint") }}</p>
      </section>

      <!-- Technical check history -->
      <section v-else-if="activeTab === 'technical'">
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.technical.title") }}</h2>
            <p class="text-sm text-muted">{{ t("car.technical.subtitle") }}</p>
          </div>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddTechnicalCheck">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.technical.add") }}
          </button>
        </div>

        <div v-if="technicalChecks.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.technical.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th>{{ t("car.technical.colDate") }}</th>
                <th>{{ t("car.technical.colResult") }}</th>
                <th>{{ t("car.technical.colNextCheck") }}</th>
                <th>{{ t("car.technical.colStatus") }}</th>
                <th>{{ t("car.technical.colStation") }}</th>
                <th class="!text-right">{{ t("car.technical.colCost") }}</th>
                <th>{{ t("car.technical.colNotes") }}</th>
                <th>{{ t("car.technical.colFile") }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="tc in technicalChecks" :key="tc.id" class="transition-colors hover:bg-sunken">
                <td class="whitespace-nowrap px-4 py-3 data font-medium text-strong">{{ formatDate(tc.date) }}</td>
                <td class="whitespace-nowrap px-4 py-3">
                  <span :class="tc.result === 'failed' ? 'dh-badge dh-badge-danger' : 'dh-badge dh-badge-success'">
                    {{ tc.result === 'failed' ? t("car.technical.failed") : t("car.technical.passed") }}
                  </span>
                </td>
                <td class="whitespace-nowrap px-4 py-3 data text-muted">{{ formatDate(tc.nextCheckDate) }}</td>
                <td class="whitespace-nowrap px-4 py-3">
                  <span :class="expiryStatus(tc).classes">{{ expiryStatus(tc).label }}</span>
                </td>
                <td class="px-4 py-3 text-body">{{ tc.station || t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-body">{{ tc.cost ? formatMoney(tc.cost) : t("common.empty") }}</td>
                <td class="px-4 py-3 text-body">{{ tc.notes || t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3">
                  <button v-if="tc.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('technical', tc)">
                    {{ t("common.download") }}
                  </button>
                  <span v-else class="text-xs text-muted">{{ t("common.empty") }}</span>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditTechnicalCheck(tc)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deleteTechnicalCheck(tc.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Maintenance -->
      <section v-else-if="activeTab === 'maintenance'">
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.maintenance.title") }}</h2>
            <p class="text-sm text-muted">{{ t("car.maintenance.subtitle") }}</p>
          </div>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddMaintenance">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.maintenance.add") }}
          </button>
        </div>

        <div v-if="maintenance.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.maintenance.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th>{{ t("car.maintenance.colDate") }}</th>
                <th>{{ t("car.maintenance.colKm") }}</th>
                <th>{{ t("car.maintenance.colType") }}</th>
                <th>{{ t("car.maintenance.colWork") }}</th>
                <th>{{ t("car.maintenance.colWorkshop") }}</th>
                <th>{{ t("car.maintenance.colStatus") }}</th>
                <th class="!text-right">{{ t("car.maintenance.colCost") }}</th>
                <th>{{ t("car.maintenance.colFile") }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="m in maintenance" :key="m.id" class="transition-colors hover:bg-sunken">
                <td class="whitespace-nowrap px-4 py-3 data font-medium text-strong">{{ formatDate(m.date) }}</td>
                <td class="whitespace-nowrap px-4 py-3 data text-body">{{ formatKm(m.km) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-body">{{ maintenanceTypeLabel(m.type) }}</td>
                <td class="px-4 py-3 text-body">
                  <div class="font-medium text-strong">{{ m.description }}</div>
                  <div v-if="m.partsUsed" class="text-xs text-muted">{{ m.partsUsed }}</div>
                  <div v-if="m.warrantyActive" class="mt-0.5 text-xs text-success">
                    {{ t("car.maintenance.underWarranty", { days: m.warrantyDaysLeft }) }}
                  </div>
                </td>
                <td class="px-4 py-3 text-body">
                  {{ m.workshop || t("common.empty") }}
                  <div v-if="m.location" class="text-xs text-muted">{{ m.location }}</div>
                </td>
                <td class="whitespace-nowrap px-4 py-3">
                  <span :class="MAINTENANCE_STATUS[m.status] || 'dh-badge dh-badge-neutral'">
                    {{ maintenanceStatusLabel(m.status) }}
                  </span>
                </td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-body">
                  {{ m.totalCost ? formatMoney(m.totalCost) : t("common.empty") }}
                </td>
                <td class="whitespace-nowrap px-4 py-3">
                  <button v-if="m.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('maintenance', m)">
                    {{ t("common.download") }}
                  </button>
                  <span v-else class="text-xs text-muted">{{ t("common.empty") }}</span>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditMaintenance(m)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deleteMaintenance(m.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Fuel -->
      <section v-else-if="activeTab === 'fuel'">
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.fuel.title") }}</h2>
            <p class="text-sm text-muted">{{ t("car.fuel.subtitle") }}</p>
          </div>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddFuel">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.fuel.add") }}
          </button>
        </div>

        <!-- Rollup -->
        <div v-if="fuelStats && fuelStats.entries > 0" class="dh-card mb-4 p-6">
          <dl class="grid grid-cols-2 gap-4 text-sm sm:grid-cols-4">
            <div>
              <dt class="eyebrow">{{ t("car.fuel.average") }}</dt>
              <dd class="mt-0.5 data text-lg font-bold text-strong">{{ formatConsumption(fuelStats.avgConsumptionL100) }}</dd>
              <dd class="text-xs text-muted">{{ formatKmPerLiter(fuelStats.avgKmPerLiter) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.best") }}</dt>
              <dd class="mt-0.5 data font-medium text-success">{{ formatConsumption(fuelStats.bestConsumptionL100) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.worst") }}</dt>
              <dd class="mt-0.5 data font-medium text-danger">{{ formatConsumption(fuelStats.worstConsumptionL100) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.costPerKm") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatMoney(fuelStats.costPerKm) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.refills") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ fuelStats.entries }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.totalLiters") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatLiters(fuelStats.totalLiters) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.totalSpent") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatMoney(fuelStats.totalCost) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.fuel.trackedDistance") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatKm(fuelStats.trackedDistanceKm) }}</dd>
              <dd class="text-xs text-muted">{{ t("car.fuel.avgPrice", { price: formatMoney(fuelStats.avgPricePerLiter) }) }}</dd>
            </div>
          </dl>
          <p v-if="!fuelStats.avgConsumptionL100" class="mt-4 text-xs text-muted">
            {{ t("car.fuel.needTwoTanks") }}
          </p>
        </div>

        <div v-if="fuel.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.fuel.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th>{{ t("car.fuel.colDate") }}</th>
                <th>{{ t("car.fuel.colKm") }}</th>
                <th class="!text-right">{{ t("car.fuel.colLiters") }}</th>
                <th class="!text-right">{{ t("car.fuel.colCost") }}</th>
                <th class="!text-right">{{ t("car.fuel.colPerLiter") }}</th>
                <th class="!text-right">{{ t("car.fuel.colDistance") }}</th>
                <th class="!text-right">{{ t("car.fuel.colConsumption") }}</th>
                <th>{{ t("car.fuel.colStation") }}</th>
                <th>{{ t("car.fuel.colFile") }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="f in fuel" :key="f.id" class="transition-colors hover:bg-sunken">
                <td class="whitespace-nowrap px-4 py-3 data font-medium text-strong">
                  {{ formatDate(f.date) }}
                  <span v-if="!f.fullTank" class="ml-1 text-xs font-normal text-muted">{{ t("car.fuel.partial") }}</span>
                  <span v-if="f.missedFill" class="ml-1 text-xs font-normal text-warning">{{ t("car.fuel.gap") }}</span>
                </td>
                <td class="whitespace-nowrap px-4 py-3 data text-body">{{ formatKm(f.km) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-body">{{ formatLiters(f.liters) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-body">{{ f.cost ? formatMoney(f.cost) : t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-muted">{{ formatMoney(f.pricePerLiter) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-muted">{{ f.distanceKm ? formatKm(f.distanceKm) : t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data font-medium" :class="f.consumptionL100 ? 'text-strong' : 'text-muted'">
                  {{ formatConsumption(f.consumptionL100) }}
                </td>
                <td class="px-4 py-3 text-body">
                  {{ f.station || t("common.empty") }}
                  <div v-if="f.notes" class="text-xs text-muted">{{ f.notes }}</div>
                </td>
                <td class="whitespace-nowrap px-4 py-3">
                  <button v-if="f.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('fuel', f)">
                    {{ t("common.download") }}
                  </button>
                  <span v-else class="text-xs text-muted">{{ t("common.empty") }}</span>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditFuel(f)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deleteFuel(f.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Charging cost. The EV counterpart of Fuel: energy in place of litres,
           and a charge to the usual full point in place of the full tank. -->
      <section v-else-if="activeTab === 'charging'">
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.charging.title") }}</h2>
            <p class="text-sm text-muted">{{ t("car.charging.subtitle") }}</p>
          </div>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddCharging">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.charging.add") }}
          </button>
        </div>

        <!-- Rollup -->
        <div v-if="chargingStats && chargingStats.entries > 0" class="dh-card mb-4 p-6">
          <dl class="grid grid-cols-2 gap-4 text-sm sm:grid-cols-4">
            <div>
              <dt class="eyebrow">{{ t("car.charging.average") }}</dt>
              <dd class="mt-0.5 data text-lg font-bold text-strong">{{ formatConsumptionKwh(chargingStats.avgConsumptionKwh100) }}</dd>
              <dd class="text-xs text-muted">{{ formatKmPerKwh(chargingStats.avgKmPerKwh) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.best") }}</dt>
              <dd class="mt-0.5 data font-medium text-success">{{ formatConsumptionKwh(chargingStats.bestConsumptionKwh100) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.worst") }}</dt>
              <dd class="mt-0.5 data font-medium text-danger">{{ formatConsumptionKwh(chargingStats.worstConsumptionKwh100) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.costPerKm") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatMoney(chargingStats.costPerKm) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.sessions") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ chargingStats.entries }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.totalKwh") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatKwh(chargingStats.totalKwh) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.totalSpent") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatMoney(chargingStats.totalCost) }}</dd>
            </div>
            <div>
              <dt class="eyebrow">{{ t("car.charging.trackedDistance") }}</dt>
              <dd class="mt-0.5 data font-medium text-strong">{{ formatKm(chargingStats.trackedDistanceKm) }}</dd>
              <dd class="text-xs text-muted">{{ t("car.charging.avgPrice", { price: formatMoney(chargingStats.avgPricePerKwh) }) }}</dd>
            </div>
          </dl>
          <p v-if="!chargingStats.avgConsumptionKwh100" class="mt-4 text-xs text-muted">
            {{ t("car.charging.needTwoCharges") }}
          </p>
        </div>

        <div v-if="charging.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.charging.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th>{{ t("car.charging.colDate") }}</th>
                <th>{{ t("car.charging.colKm") }}</th>
                <th class="!text-right">{{ t("car.charging.colKwh") }}</th>
                <th class="!text-right">{{ t("car.charging.colCost") }}</th>
                <th class="!text-right">{{ t("car.charging.colPerKwh") }}</th>
                <th class="!text-right">{{ t("car.charging.colDistance") }}</th>
                <th class="!text-right">{{ t("car.charging.colConsumption") }}</th>
                <th>{{ t("car.charging.colLocation") }}</th>
                <th>{{ t("car.charging.colFile") }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="c in charging" :key="c.id" class="transition-colors hover:bg-sunken">
                <td class="whitespace-nowrap px-4 py-3 data font-medium text-strong">
                  {{ formatDate(c.date) }}
                  <span v-if="!c.fullCharge" class="ml-1 text-xs font-normal text-muted">{{ t("car.charging.partial") }}</span>
                  <span v-if="c.missedSession" class="ml-1 text-xs font-normal text-warning">{{ t("car.charging.gap") }}</span>
                </td>
                <td class="whitespace-nowrap px-4 py-3 data text-body">{{ formatKm(c.km) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-body">{{ formatKwh(c.kwh) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-body">{{ c.cost ? formatMoney(c.cost) : t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-muted">{{ formatMoney(c.pricePerKwh) }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data text-muted">{{ c.distanceKm ? formatKm(c.distanceKm) : t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 text-right data font-medium" :class="c.consumptionKwh100 ? 'text-strong' : 'text-muted'">
                  {{ formatConsumptionKwh(c.consumptionKwh100) }}
                </td>
                <td class="px-4 py-3 text-body">
                  {{ c.location || t("common.empty") }}
                  <div v-if="c.notes" class="text-xs text-muted">{{ c.notes }}</div>
                </td>
                <td class="whitespace-nowrap px-4 py-3">
                  <button v-if="c.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('charging', c)">
                    {{ t("common.download") }}
                  </button>
                  <span v-else class="text-xs text-muted">{{ t("common.empty") }}</span>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditCharging(c)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deleteCharging(c.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Documents -->
      <section v-else-if="activeTab === 'documents'">
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.documents.title") }}</h2>
            <p class="text-sm text-muted">{{ t("car.documents.subtitle") }}</p>
          </div>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddDocument">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.documents.add") }}
          </button>
        </div>

        <div v-if="documents.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.documents.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th>{{ t("car.documents.colType") }}</th>
                <th>{{ t("car.documents.colTitle") }}</th>
                <th>{{ t("car.documents.colProvider") }}</th>
                <th>{{ t("car.documents.colIssued") }}</th>
                <th>{{ t("car.documents.colRenewal") }}</th>
                <th>{{ t("car.documents.colStatus") }}</th>
                <th>{{ t("car.documents.colFile") }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="d in documents" :key="d.id" class="transition-colors hover:bg-sunken">
                <td class="whitespace-nowrap px-4 py-3 text-body">{{ documentTypeLabel(d.type) }}</td>
                <td class="px-4 py-3">
                  <div class="font-medium text-strong">{{ d.title }}</div>
                  <div v-if="d.reference" class="data text-xs text-muted">{{ d.reference }}</div>
                </td>
                <td class="px-4 py-3 text-body">{{ d.provider || t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 data text-muted">{{ d.issueDate ? formatDate(d.issueDate) : t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3 data text-body">{{ d.expiryDate ? formatDate(d.expiryDate) : t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3">
                  <span :class="expiryStatus(d).classes">{{ expiryStatus(d).label }}</span>
                </td>
                <td class="whitespace-nowrap px-4 py-3">
                  <button v-if="d.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('documents', d)">
                    {{ t("common.download") }}
                  </button>
                  <span v-else class="text-xs text-muted">{{ t("common.empty") }}</span>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditDocument(d)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deleteDocument(d.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Reminders -->
      <section v-else-if="activeTab === 'reminders'">
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.reminders.title") }}</h2>
            <p class="text-sm text-muted">{{ t("car.reminders.subtitle") }}</p>
          </div>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddReminder">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.reminders.add") }}
          </button>
        </div>

        <div v-if="reminders.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.reminders.empty") }}
        </div>

        <ul v-else class="space-y-2">
          <li
            v-for="r in reminders"
            :key="r.id"
            class="dh-card flex flex-wrap items-center justify-between gap-3 p-4"
            :class="r.done ? 'opacity-60' : ''">
            <div class="min-w-0">
              <div class="flex flex-wrap items-center gap-2">
                <span class="font-medium text-strong" :class="r.done ? 'line-through' : ''">{{ r.title }}</span>
                <span class="dh-badge dh-badge-neutral">{{ reminderTypeLabel(r.type) }}</span>
                <span v-if="r.auto" class="dh-badge dh-badge-neutral">{{ t("car.reminders.automatic") }}</span>
                <span v-if="r.repeatDays || r.repeatKm" class="dh-badge dh-badge-neutral">{{ t("car.reminders.repeats") }}</span>
              </div>
              <p class="mt-0.5 text-xs text-muted">
                <span v-if="r.dueDate" class="data">{{ formatDate(r.dueDate) }}</span>
                <span v-if="r.dueDate && r.dueKm"> · </span>
                <span v-if="r.dueKm" class="data">{{ t("car.reminders.at", { km: formatKm(r.dueKm) }) }}</span>
                <span v-if="r.notes"> · {{ r.notes }}</span>
              </p>
            </div>
            <div class="flex items-center gap-3">
              <span :class="reminderStatus(r).classes">{{ reminderStatus(r).label }}</span>
              <!-- Auto reminders have no row behind them: they clear by renewing
                   the document or logging the service they came from. -->
              <template v-if="canWrite && !r.auto">
                <button v-if="!r.done" class="text-xs font-medium text-success hover:underline" @click="completeReminder(r)">
                  {{ r.repeatDays || r.repeatKm ? t("car.reminders.doneRollForward") : t("car.reminders.markDone") }}
                </button>
                <button v-else class="text-xs font-medium text-brandtext hover:underline" @click="reopenReminder(r)">{{ t("car.reminders.reopen") }}</button>
                <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditReminder(r)">{{ t("common.edit") }}</button>
                <button class="text-xs font-medium text-danger hover:underline" @click="deleteReminder(r.id)">{{ t("common.delete") }}</button>
              </template>
            </div>
          </li>
        </ul>
      </section>

      <!-- Parts catalog -->
      <section v-else-if="activeTab === 'parts'">
        <div class="mb-3 flex items-center justify-between">
          <h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("car.parts.title") }}</h2>
          <button v-if="canWrite" class="dh-btn dh-btn-primary !px-3 !py-1.5" @click="openAddPart">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14M5 12h14" /></svg>
            {{ t("car.parts.add") }}
          </button>
        </div>

        <div v-if="parts.length === 0" class="rounded-card border border-dashed border-default p-8 text-center text-muted">
          {{ t("car.parts.empty") }}
        </div>

        <div v-else class="dh-card overflow-x-auto p-0">
          <table class="min-w-full text-sm">
            <thead class="bg-sunken text-left">
              <tr class="[&>th]:eyebrow [&>th]:px-4 [&>th]:py-3">
                <th>{{ t("car.parts.colPart") }}</th>
                <th>{{ t("car.parts.colPartNumber") }}</th>
                <th>{{ t("car.parts.colNotes") }}</th>
                <th>{{ t("car.parts.colFile") }}</th>
                <th v-if="canWrite"></th>
              </tr>
            </thead>
            <tbody class="divide-y divide-subtle">
              <tr v-for="p in parts" :key="p.id" class="transition-colors hover:bg-sunken">
                <td class="px-4 py-3 font-medium text-strong">{{ p.name }}</td>
                <td class="px-4 py-3 data text-body">{{ p.partNumber || t("common.empty") }}</td>
                <td class="px-4 py-3 text-body">{{ p.notes || t("common.empty") }}</td>
                <td class="whitespace-nowrap px-4 py-3">
                  <button v-if="p.hasFile" class="text-xs font-medium text-brandtext hover:underline" @click="downloadAttachment('parts', p)">
                    {{ t("common.download") }}
                  </button>
                  <span v-else class="text-xs text-muted">{{ t("common.empty") }}</span>
                </td>
                <td v-if="canWrite" class="whitespace-nowrap px-4 py-3 text-right">
                  <button class="text-xs font-medium text-brandtext hover:underline" @click="openEditPart(p)">{{ t("common.edit") }}</button>
                  <button class="ml-3 text-xs font-medium text-danger hover:underline" @click="deletePart(p.id)">{{ t("common.delete") }}</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>
    </template>

    <!-- Modals -->
    <CarFormModal v-if="showCarEdit" :car="car" @saved="onCarSaved" @close="showCarEdit = false" />
    <ServiceFormModal
      v-if="showService"
      :car-id="id"
      :car="car"
      :service="editingService"
      @saved="onServiceSaved"
      @close="showService = false"
    />
    <TechnicalCheckFormModal
      v-if="showTechnicalCheck"
      :car-id="id"
      :car="car"
      :check="editingTechnicalCheck"
      @saved="onTechnicalCheckSaved"
      @close="showTechnicalCheck = false"
    />
    <PartFormModal
      v-if="showPart"
      :car-id="id"
      :part="editingPart"
      @saved="onPartSaved"
      @close="showPart = false"
    />
    <FuelFormModal
      v-if="showFuel"
      :car-id="id"
      :entry="editingFuel"
      @saved="onFuelSaved"
      @close="showFuel = false"
    />
    <ChargingFormModal
      v-if="showCharging"
      :car-id="id"
      :entry="editingCharging"
      @saved="onChargingSaved"
      @close="showCharging = false"
    />
    <MaintenanceFormModal
      v-if="showMaintenance"
      :car-id="id"
      :entry="editingMaintenance"
      @saved="onMaintenanceSaved"
      @close="showMaintenance = false"
    />
    <DocumentFormModal
      v-if="showDocument"
      :car-id="id"
      :doc="editingDocument"
      @saved="onDocumentSaved"
      @close="showDocument = false"
    />
    <ReminderFormModal
      v-if="showReminder"
      :car-id="id"
      :car="car"
      :reminder="editingReminder"
      @saved="onReminderSaved"
      @close="showReminder = false"
    />
    <ShareModal v-if="showShare && car" :car="car" @close="showShare = false" />

    <!-- What this car's page shows: tabs, and the Information rows -->
    <Modal v-if="showViewPicker" :title="t('car.viewPicker.title')" @close="showViewPicker = false">
      <p class="mb-4 text-sm text-muted">{{ t("car.viewPicker.subtitle") }}</p>

      <p class="eyebrow mb-2">{{ t("car.viewPicker.tabsHeading") }}</p>
      <div class="grid gap-2 sm:grid-cols-2">
        <label
          v-for="key in tabPickerKeys"
          :key="key"
          class="flex items-center gap-2 text-sm font-medium text-body"
        >
          <input
            type="checkbox"
            class="h-4 w-4 rounded border-subtle text-accent focus:ring-accent"
            :checked="tabDraft.includes(key)"
            @change="toggleTabDraft(key, $event.target.checked)"
          />
          <span>{{ tabPickerLabel(key) }}</span>
        </label>
      </div>
      <p class="mt-2 text-xs text-muted">{{ t("car.viewPicker.alwaysOn", { tab: t("car.tabs.info") }) }}</p>

      <p class="eyebrow mb-2 mt-5">{{ t("car.viewPicker.fieldsHeading") }}</p>
      <div class="grid gap-2 sm:grid-cols-2">
        <label
          v-for="key in fieldKeys"
          :key="key"
          class="flex items-center gap-2 text-sm font-medium text-body"
        >
          <input
            type="checkbox"
            class="h-4 w-4 rounded border-subtle text-accent focus:ring-accent"
            :checked="fieldDraft.includes(key)"
            @change="toggleFieldDraft(key, $event.target.checked)"
          />
          <span>{{ infoFieldLabel(key) }}</span>
        </label>
      </div>
      <p class="mt-2 text-xs text-muted">{{ t("car.viewPicker.fieldsOrderHint") }}</p>

      <p class="eyebrow mb-2 mt-5">{{ t("car.viewPicker.serviceColumnsHeading") }}</p>
      <div class="grid gap-2 sm:grid-cols-2">
        <label
          v-for="key in serviceColumnKeys.filter((k) => k !== 'date')"
          :key="key"
          class="flex items-center gap-2 text-sm font-medium text-body"
        >
          <input
            type="checkbox"
            class="h-4 w-4 rounded border-subtle text-accent focus:ring-accent"
            :checked="columnDraft.includes(key)"
            @change="toggleColumnDraft(key, $event.target.checked)"
          />
          <span>{{ serviceColumnLabel(key) }}</span>
        </label>
      </div>
      <p class="mt-2 text-xs text-muted">
        {{ t("car.viewPicker.columnAlwaysOn", { column: t("car.services.colDate") }) }}
        {{ t("car.viewPicker.serviceColumnsOrderHint") }}
      </p>

      <p class="eyebrow mb-2 mt-5">{{ t("car.viewPicker.servicePartsHeading") }}</p>
      <div class="grid gap-2 sm:grid-cols-2">
        <label
          v-for="part in SERVICE_PARTS"
          :key="part.key"
          class="flex items-center gap-2 text-sm font-medium text-body"
        >
          <input
            type="checkbox"
            class="h-4 w-4 rounded border-subtle text-accent focus:ring-accent"
            :checked="partDraft.includes(part.key)"
            @change="togglePartDraft(part.key, $event.target.checked)"
          />
          <span>{{ t(part.label) }}</span>
        </label>
      </div>
      <p class="mt-2 text-xs text-muted">{{ t("car.viewPicker.servicePartsHint") }}</p>

      <p v-if="viewError" class="mt-3 text-sm text-danger">{{ viewError }}</p>

      <div class="mt-5 flex justify-end gap-2">
        <button class="dh-btn dh-btn-ghost" @click="showViewPicker = false">{{ t("common.cancel") }}</button>
        <button class="dh-btn dh-btn-primary" :disabled="viewSaving" @click="saveView">
          {{ viewSaving ? t("common.saving") : t("common.save") }}
        </button>
      </div>
    </Modal>

    <!-- Delete-car confirmation (type-to-confirm; cascade removes all data) -->
    <div v-if="showDeleteCar && car" class="fixed inset-0 z-30 grid place-items-center bg-brand-900/40 p-4 backdrop-blur-sm" @click.self="showDeleteCar = false">
      <div class="dh-card w-full max-w-md p-6 shadow-pop">
        <h2 class="mb-2 text-lg font-bold tracking-[-0.02em] text-danger">{{ t("car.delete.title") }}</h2>
        <p class="mb-4 text-sm text-body">
          {{ t("car.delete.body", {
            name: car.name,
            services: t("car.delete.services", { n: services.length }),
            maintenance: t("car.delete.maintenance", { n: maintenance.length }),
            fuel: t("car.delete.fuel", { n: fuel.length }),
            charging: t("car.delete.charging", { n: charging.length }),
            documents: t("car.delete.documents", { n: documents.length }),
            parts: t("car.delete.parts", { n: parts.length }),
          }) }}
        </p>
        <label class="dh-label">
          {{ tSplit("car.delete.typeToConfirm", "name").before
          }}<span class="data text-strong">{{ car.name }}</span>{{ tSplit("car.delete.typeToConfirm", "name").after }}
        </label>
        <input v-model="deleteConfirmText" :placeholder="car.name" class="dh-input mb-4" />
        <div class="flex justify-end gap-2">
          <button type="button" class="dh-btn dh-btn-ghost" @click="showDeleteCar = false">{{ t("common.cancel") }}</button>
          <button type="button" :disabled="!canDeleteCar || deletingCar" class="dh-btn dh-btn-danger" @click="confirmDeleteCar">
            {{ deletingCar ? t("car.delete.deleting") : t("car.delete.confirm") }}
          </button>
        </div>
      </div>
    </div>
  </div>
</template>
