"use client";

import { DragEvent, KeyboardEvent, useEffect, useMemo, useRef, useState } from "react";
import {
  createCircuit,
  createCircuitDeviceRow,
  deleteCircuitById,
  deleteCircuitDeviceRowById,
  getCircuitTree,
  getNextCircuitIdentifier,
  listProjectDevices,
  moveCircuitDeviceRowsBulk,
  moveCircuitDeviceRowById,
  reorderSectionCircuits,
  renumberCircuitSection,
  updateSectionEquipmentIdentifiers,
  updateCircuitById,
  updateCircuitDeviceRowById,
} from "../utils/api";
import type {
  CircuitTreeCircuitDto,
  CircuitTreeDeviceRowDto,
  CircuitTreeResponseDto,
  ProjectDeviceDto,
} from "../types";

type CellKey =
  | "equipmentIdentifier"
  | "displayName"
  | "quantity"
  | "powerPerUnit"
  | "simultaneityFactor"
  | "rowTotalPower"
  | "circuitTotalPower"
  | "protectionSummary"
  | "cableSummary"
  | "roomSummary"
  | "remark"
  | "technicalName"
  | "connectionKind"
  | "phaseType"
  | "costGroup"
  | "category"
  | "level"
  | "roomNumberSnapshot"
  | "roomNameSnapshot"
  | "cosPhi"
  | "protectionType"
  | "protectionRatedCurrent"
  | "protectionCharacteristic"
  | "cableType"
  | "cableCrossSection"
  | "cableLength"
  | "rcdAssignment"
  | "terminalDesignation"
  | "status"
  | "isReserve";

type SaveDirection = "stay" | "next" | "prev";
type StartEditMode = "selectExisting" | "replaceWithTypedChar";
type RowType = "section" | "circuitCompact" | "circuitSummary" | "deviceRow" | "reserveCircuit" | "placeholder";
type CellKind = "circuitField" | "deviceField" | "computed" | "readonly";
type SortDirection = "asc" | "desc";

interface SelectedCell {
  rowKey: string;
  cellKey: CellKey;
}

interface EditingCell extends SelectedCell {
  draft: string;
  mode: StartEditMode;
  focusToken: number;
}

interface SelectionIntent {
  rowKey: string;
  cellKey: CellKey;
  rowType: RowType;
  sectionId: string;
  circuitId?: string;
  deviceId?: string;
}

interface VisibleGridCell {
  cellKey: CellKey;
  editable: boolean;
  kind: CellKind;
  value: string | number | boolean | undefined;
}

interface VisibleGridRow {
  rowKey: string;
  rowType: RowType;
  sectionId: string;
  circuit?: CircuitTreeCircuitDto;
  device?: CircuitTreeDeviceRowDto;
  cells: VisibleGridCell[];
}

interface HistoryCommand {
  label: string;
  redo: () => Promise<SelectionIntent | null | void>;
  undo: () => Promise<SelectionIntent | null | void>;
}

interface CircuitSnapshot {
  id: string;
  sectionId: string;
  equipmentIdentifier: string;
  displayName?: string;
  sortOrder: number;
  protectionType?: string;
  protectionRatedCurrent?: number;
  protectionCharacteristic?: string;
  cableType?: string;
  cableCrossSection?: string;
  cableLength?: number;
  rcdAssignment?: string;
  terminalDesignation?: string;
  voltage?: number;
  status?: string;
  isReserve: boolean;
  remark?: string;
  deviceRows: CircuitTreeDeviceRowDto[];
}

type ProjectDeviceDropIntent =
  | { kind: "new-circuit"; sectionId: string }
  | { kind: "add-to-circuit"; circuitId: string; sectionId: string };

type DeviceRowMoveDropIntent =
  | { kind: "move-to-circuit"; circuitId: string; sectionId: string }
  | { kind: "move-to-new-circuit"; sectionId: string };

type CircuitReorderDropIntent =
  | { kind: "before-circuit"; sectionId: string; targetCircuitId: string; valid: boolean }
  | { kind: "after-circuit"; sectionId: string; targetCircuitId: string; valid: boolean }
  | { kind: "section-end"; sectionId: string; valid: boolean };

const COLUMN_LAYOUT_STORAGE_KEY = "circuitTreeEditor.columnLayout.v1";

interface ColumnDef {
  key: CellKey;
  label: string;
  numeric?: boolean;
  defaultVisible?: boolean;
  locked?: boolean;
}

// Centralized column metadata keeps render, filtering/sorting, keyboard traversal,
// and persisted layout in sync. BMK/equipmentIdentifier stays locked as first column
// because circuit identity and circuit drag handles depend on always-visible BMK context.
const allColumns: ColumnDef[] = [
  { key: "equipmentIdentifier", label: "Equipment identifier", defaultVisible: true, locked: true },
  { key: "displayName", label: "Display name", defaultVisible: true },
  { key: "quantity", label: "Quantity", numeric: true, defaultVisible: true },
  { key: "powerPerUnit", label: "Power / unit", numeric: true, defaultVisible: true },
  { key: "simultaneityFactor", label: "Simultaneity", numeric: true, defaultVisible: true },
  { key: "rowTotalPower", label: "Row total", numeric: true, defaultVisible: true },
  { key: "circuitTotalPower", label: "Circuit total", numeric: true, defaultVisible: true },
  { key: "protectionSummary", label: "Protection summary", defaultVisible: true },
  { key: "cableSummary", label: "Cable summary", defaultVisible: true },
  { key: "roomSummary", label: "Room", defaultVisible: true },
  { key: "remark", label: "Remark", defaultVisible: true },
  { key: "technicalName", label: "Technical name" },
  { key: "connectionKind", label: "Connection kind" },
  { key: "phaseType", label: "Phase type" },
  { key: "costGroup", label: "Cost group" },
  { key: "category", label: "Category" },
  { key: "level", label: "Level" },
  { key: "roomNumberSnapshot", label: "Room number" },
  { key: "roomNameSnapshot", label: "Room name" },
  { key: "cosPhi", label: "cosPhi", numeric: true },
  { key: "protectionType", label: "Protection type" },
  { key: "protectionRatedCurrent", label: "Protection rated current", numeric: true },
  { key: "protectionCharacteristic", label: "Protection characteristic" },
  { key: "cableType", label: "Cable type" },
  { key: "cableCrossSection", label: "Cable cross-section" },
  { key: "cableLength", label: "Cable length", numeric: true },
  { key: "rcdAssignment", label: "RCD assignment" },
  { key: "terminalDesignation", label: "Terminal designation" },
  { key: "status", label: "Status" },
  { key: "isReserve", label: "Reserve" },
];

const defaultVisibleColumnKeys = allColumns.filter((column) => column.defaultVisible).map((column) => column.key);

const deviceOnlyColumns = new Set<CellKey>([
  "quantity",
  "powerPerUnit",
  "simultaneityFactor",
  "rowTotalPower",
  "roomSummary",
  "technicalName",
  "connectionKind",
  "phaseType",
  "costGroup",
  "category",
  "level",
  "roomNumberSnapshot",
  "roomNameSnapshot",
  "cosPhi",
]);
const circuitOnlyColumns = new Set<CellKey>([
  "equipmentIdentifier",
  "protectionSummary",
  "cableSummary",
  "circuitTotalPower",
  "protectionType",
  "protectionRatedCurrent",
  "protectionCharacteristic",
  "cableType",
  "cableCrossSection",
  "cableLength",
  "rcdAssignment",
  "terminalDesignation",
  "status",
  "isReserve",
]);

const deviceFieldKeys = new Set<CellKey>([
  "displayName",
  "technicalName",
  "connectionKind",
  "phaseType",
  "costGroup",
  "category",
  "level",
  "roomSummary",
  "roomNumberSnapshot",
  "roomNameSnapshot",
  "quantity",
  "powerPerUnit",
  "simultaneityFactor",
  "cosPhi",
  "remark",
]);

const circuitFieldKeys = new Set<CellKey>([
  "equipmentIdentifier",
  "displayName",
  "protectionType",
  "protectionRatedCurrent",
  "protectionCharacteristic",
  "protectionSummary",
  "cableType",
  "cableCrossSection",
  "cableLength",
  "cableSummary",
  "rcdAssignment",
  "terminalDesignation",
  "status",
  "isReserve",
  "remark",
]);

function formatValue(value: string | number | boolean | undefined) {
  if (value === undefined || value === null || value === "") {
    return "-";
  }
  if (typeof value === "boolean") {
    return value ? "Yes" : "No";
  }
  return String(value);
}

function normalizeUiError(err: unknown): string {
  const message = err instanceof Error ? err.message : "Operation failed.";
  try {
    const parsed = JSON.parse(message) as { error?: string };
    if (parsed.error?.includes("Duplicate equipmentIdentifier")) {
      return "Das Betriebsmittelkennzeichen ist in dieser Stromkreisliste bereits vorhanden.";
    }
    if (parsed.error) {
      return parsed.error;
    }
  } catch {
    // ignore
  }
  if (message.includes("Duplicate equipmentIdentifier")) {
    return "Das Betriebsmittelkennzeichen ist in dieser Stromkreisliste bereits vorhanden.";
  }
  if (message.includes("Invalid number")) {
    return "Bitte einen gültigen Zahlenwert eingeben.";
  }
  return message;
}

function parseNumeric(cellKey: CellKey, draft: string): number | undefined {
  const trimmed = draft.trim();
  if (trimmed === "") {
    return undefined;
  }
  const parsed = Number(trimmed);
  if (Number.isNaN(parsed)) {
    throw new Error(`Invalid number in ${cellKey}`);
  }
  return parsed;
}

function getDeviceValue(device: CircuitTreeDeviceRowDto, key: CellKey): string | number | boolean | undefined {
  switch (key) {
    case "technicalName":
      return device.name;
    case "displayName":
      return device.displayName || device.name;
    case "phaseType":
      return device.phaseType;
    case "connectionKind":
      return device.connectionKind;
    case "costGroup":
      return device.costGroup;
    case "category":
      return device.category;
    case "level":
      return device.level;
    case "roomSummary":
      return [device.roomNumberSnapshot, device.roomNameSnapshot].filter(Boolean).join(" ").trim() || undefined;
    case "roomNumberSnapshot":
      return device.roomNumberSnapshot;
    case "roomNameSnapshot":
      return device.roomNameSnapshot;
    case "quantity":
      return device.quantity;
    case "powerPerUnit":
      return device.powerPerUnit;
    case "simultaneityFactor":
      return device.simultaneityFactor;
    case "cosPhi":
      return device.cosPhi;
    case "rowTotalPower":
      return device.rowTotalPower;
    case "remark":
      return device.remark;
    default:
      return undefined;
  }
}

function getCircuitValue(circuit: CircuitTreeCircuitDto, key: CellKey): string | number | boolean | undefined {
  switch (key) {
    case "equipmentIdentifier":
      return circuit.equipmentIdentifier;
    case "displayName":
      return circuit.displayName;
    case "circuitTotalPower":
      return circuit.circuitTotalPower;
    case "protectionType":
      return circuit.protectionType;
    case "protectionRatedCurrent":
      return circuit.protectionRatedCurrent;
    case "protectionCharacteristic":
      return circuit.protectionCharacteristic;
    case "protectionSummary": {
      const current =
        circuit.protectionRatedCurrent !== undefined && circuit.protectionRatedCurrent !== null
          ? `${circuit.protectionRatedCurrent}A`
          : "";
      return [circuit.protectionType, current, circuit.protectionCharacteristic]
        .filter(Boolean)
        .join(" ")
        .trim() || undefined;
    }
    case "cableSummary": {
      const length =
        circuit.cableLength !== undefined && circuit.cableLength !== null ? `${circuit.cableLength} m` : "";
      return [circuit.cableType, circuit.cableCrossSection, length].filter(Boolean).join(", ").trim() || undefined;
    }
    case "cableType":
      return circuit.cableType;
    case "cableCrossSection":
      return circuit.cableCrossSection;
    case "cableLength":
      return circuit.cableLength;
    case "rcdAssignment":
      return circuit.rcdAssignment;
    case "terminalDesignation":
      return circuit.terminalDesignation;
    case "status":
      return circuit.status;
    case "isReserve":
      return circuit.isReserve;
    case "remark":
      return circuit.remark;
    default:
      return undefined;
  }
}

function normalizeFilterValue(value: string | number | boolean | undefined) {
  return formatValue(value);
}

function getBlockSortValue(circuit: CircuitTreeCircuitDto, key: CellKey) {
  const firstRow = circuit.deviceRows[0];
  if (circuitOnlyColumns.has(key)) {
    return getCircuitValue(circuit, key);
  }
  if (deviceOnlyColumns.has(key)) {
    return firstRow ? getDeviceValue(firstRow, key) : undefined;
  }
  if (key === "displayName" || key === "remark") {
    if (circuit.displayName || circuit.remark) {
      return getCircuitValue(circuit, key);
    }
    return firstRow ? getDeviceValue(firstRow, key) : undefined;
  }
  return getCircuitValue(circuit, key);
}

function compareSortValues(a: string | number | boolean | undefined, b: string | number | boolean | undefined) {
  const av = a === undefined || a === null ? "" : a;
  const bv = b === undefined || b === null ? "" : b;
  if (typeof av === "number" && typeof bv === "number") {
    return av - bv;
  }
  return String(av).localeCompare(String(bv), undefined, { sensitivity: "base", numeric: true });
}

function getCellKind(rowType: RowType, key: CellKey): CellKind {
  if (key === "rowTotalPower" || key === "circuitTotalPower") {
    return "computed";
  }
  if (rowType === "section") {
    return "readonly";
  }
  if (rowType === "placeholder") {
    if (deviceFieldKeys.has(key)) {
      return "deviceField";
    }
    if (circuitFieldKeys.has(key)) {
      return "circuitField";
    }
    return "readonly";
  }
  if (rowType === "deviceRow") {
    return deviceFieldKeys.has(key) ? "deviceField" : "readonly";
  }
  if (rowType === "circuitSummary") {
    return circuitFieldKeys.has(key) ? "circuitField" : "readonly";
  }
  if (rowType === "reserveCircuit") {
    if (deviceFieldKeys.has(key)) {
      return "deviceField";
    }
    if (circuitFieldKeys.has(key)) {
      return "circuitField";
    }
    return "readonly";
  }
  if (rowType === "circuitCompact") {
    if (deviceFieldKeys.has(key)) {
      return "deviceField";
    }
    if (circuitFieldKeys.has(key)) {
      return "circuitField";
    }
    return "readonly";
  }
  return "readonly";
}

function isPrintableKey(event: KeyboardEvent<HTMLElement>) {
  return event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey;
}

export function CircuitTreeEditor(props: { projectId: string; circuitListId: string }) {
  /*
    Circuit-tree editor invariant overview:
    - Data model is circuit-first (section -> circuit -> device rows).
    - Rendered table rows are virtual projection rows, not direct DB rows.
    - A circuit may appear as compact row, summary+device rows, reserve row, or placeholder row.
    - Keyboard navigation and drag/drop targeting must use normalized visible rows, not raw tree nesting.
  */
  const { projectId, circuitListId } = props;
  const [data, setData] = useState<CircuitTreeResponseDto | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  // selectedCell is spreadsheet-style keyboard focus target within normalized grid.
  // It can exist without an active edit input.
  const [selectedCell, setSelectedCell] = useState<SelectedCell | null>(null);
  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
  const [anchorRowKey, setAnchorRowKey] = useState<string | null>(null);
  // editingCell is mounted input session state (draft + mode + focus token).
  // It should only exist for currently editable grid cells.
  const [editingCell, setEditingCell] = useState<EditingCell | null>(null);
  const [activeSectionId, setActiveSectionId] = useState<string | null>(null);
  const [isSaving, setIsSaving] = useState(false);
  const [projectDevices, setProjectDevices] = useState<ProjectDeviceDto[]>([]);
  const [projectDeviceSearch, setProjectDeviceSearch] = useState("");
  const [selectedProjectDeviceId, setSelectedProjectDeviceId] = useState<string | null>(null);
  const [targetSectionId, setTargetSectionId] = useState<string | null>(null);
  const [targetCircuitId, setTargetCircuitId] = useState<string | null>(null);
  // Drag intent states stay separated by domain intent to avoid cross-type accidental operations.
  // project-device drag => create/append linked rows, device-row drag => move rows,
  // circuit drag => reorder circuit blocks, column drag => layout-only changes.
  const [draggingProjectDeviceId, setDraggingProjectDeviceId] = useState<string | null>(null);
  const [dropIntent, setDropIntent] = useState<ProjectDeviceDropIntent | null>(null);
  const [draggingDeviceRowId, setDraggingDeviceRowId] = useState<string | null>(null);
  const [draggingDeviceRowIds, setDraggingDeviceRowIds] = useState<string[]>([]);
  const [deviceMoveIntent, setDeviceMoveIntent] = useState<DeviceRowMoveDropIntent | null>(null);
  const [draggingCircuitId, setDraggingCircuitId] = useState<string | null>(null);
  const [draggingCircuitIds, setDraggingCircuitIds] = useState<string[]>([]);
  const [circuitReorderIntent, setCircuitReorderIntent] = useState<CircuitReorderDropIntent | null>(null);
  // Undo/redo history is session-local UI state (not persisted server-side).
  const [undoStack, setUndoStack] = useState<HistoryCommand[]>([]);
  const [redoStack, setRedoStack] = useState<HistoryCommand[]>([]);
  const [historyBusy, setHistoryBusy] = useState(false);
  const [sortState, setSortState] = useState<{ key: CellKey; direction: SortDirection } | null>(null);
  const [columnFilters, setColumnFilters] = useState<Partial<Record<CellKey, string[]>>>({});
  const [openFilterColumn, setOpenFilterColumn] = useState<CellKey | null>(null);
  const [visibleColumnKeys, setVisibleColumnKeys] = useState<CellKey[]>(defaultVisibleColumnKeys);
  const [columnOrder, setColumnOrder] = useState<CellKey[]>(allColumns.map((column) => column.key));
  const [isColumnMenuOpen, setIsColumnMenuOpen] = useState(false);
  const [draggingColumnKey, setDraggingColumnKey] = useState<CellKey | null>(null);
  const [columnDropTargetKey, setColumnDropTargetKey] = useState<CellKey | null>(null);
  const [pendingFocus, setPendingFocus] = useState<SelectedCell | null>(null);
  const pendingSelectionAfterReload = useRef<SelectionIntent | null>(null);
  const pendingSelectedDeviceRowIdsAfterReload = useRef<string[] | null>(null);
  const pendingSelectedCircuitIdsAfterReload = useRef<string[] | null>(null);
  const containerRef = useRef<HTMLDivElement | null>(null);
  const inputRef = useRef<HTMLInputElement | null>(null);
  const focusTokenRef = useRef(1);

  const orderedColumns = useMemo(
    () => columnOrder.map((key) => allColumns.find((column) => column.key === key)).filter(Boolean) as ColumnDef[],
    [columnOrder]
  );
  const visibleColumns = useMemo(
    () => orderedColumns.filter((column) => visibleColumnKeys.includes(column.key)),
    [orderedColumns, visibleColumnKeys]
  );

  async function loadTree(options?: { showLoading?: boolean }) {
    const showLoading = options?.showLoading ?? false;
    if (showLoading) {
      setIsLoading(true);
    }
    setError(null);
    try {
      const tree = await getCircuitTree(projectId, circuitListId);
      setData(tree);
    } catch (err) {
      setError(normalizeUiError(err));
    } finally {
      if (showLoading) {
        setIsLoading(false);
      }
    }
  }

  function normalizeColumnOrder(keys: CellKey[]) {
    const unique = [...new Set(keys)];
    const allKeys = allColumns.map((column) => column.key);
    const merged = [...unique, ...allKeys.filter((key) => !unique.includes(key))];
    return ["equipmentIdentifier" as CellKey, ...merged.filter((key) => key !== "equipmentIdentifier")];
  }

  // Initial/identity-change tree load for current route context.
  useEffect(() => {
    void loadTree({ showLoading: true });
  }, [projectId, circuitListId]);

  // Loads project-device palette used for sidebar drag/drop and quick inserts.
  useEffect(() => {
    async function loadProjectDeviceList() {
      try {
        const list = await listProjectDevices(projectId);
        setProjectDevices(list);
      } catch (err) {
        setError(normalizeUiError(err));
      }
    }
    void loadProjectDeviceList();
  }, [projectId]);

  useEffect(() => {
    try {
      const raw = localStorage.getItem(COLUMN_LAYOUT_STORAGE_KEY);
      if (!raw) {
        return;
      }
      const parsed = JSON.parse(raw) as { order?: string[]; visible?: string[] };
      const validKeys = new Set(allColumns.map((column) => column.key));
      const parsedOrder = (parsed.order ?? []).filter((key): key is CellKey => validKeys.has(key as CellKey));
      const parsedVisible = (parsed.visible ?? []).filter((key): key is CellKey => validKeys.has(key as CellKey));
      if (!parsedOrder.length || !parsedVisible.length || !parsedVisible.includes("equipmentIdentifier")) {
        return;
      }
      setColumnOrder(normalizeColumnOrder(parsedOrder));
      setVisibleColumnKeys(parsedVisible);
    } catch {
      // ignore invalid local storage and use defaults
    }
  }, []);

  useEffect(() => {
    const payload = {
      order: columnOrder,
      visible: visibleColumnKeys,
    };
    localStorage.setItem(COLUMN_LAYOUT_STORAGE_KEY, JSON.stringify(payload));
  }, [columnOrder, visibleColumnKeys]);

  useEffect(() => {
    const visible = new Set(visibleColumnKeys);
    setSortState((current) => {
      if (!current) {
        return current;
      }
      return visible.has(current.key) ? current : null;
    });
    setColumnFilters((current) => {
      const next: Partial<Record<CellKey, string[]>> = {};
      for (const [key, values] of Object.entries(current)) {
        if (visible.has(key as CellKey) && values && values.length > 0) {
          next[key as CellKey] = values;
        }
      }
      return next;
    });
    setOpenFilterColumn((current) => (current && visible.has(current) ? current : null));
  }, [visibleColumnKeys]);

  const hasActiveFilters = useMemo(
    () => Object.values(columnFilters).some((values) => (values?.length ?? 0) > 0),
    [columnFilters]
  );

  const distinctValuesByColumn = useMemo(() => {
    // Filter options are generated only for visible columns to match current header UI.
    // Hidden columns still exist in row model, but are intentionally not filterable from header.
    const result = {} as Record<CellKey, string[]>;
    for (const column of visibleColumns) {
      const set = new Set<string>();
      for (const section of data?.sections ?? []) {
        for (const circuit of section.circuits) {
          set.add(normalizeFilterValue(getCircuitValue(circuit, column.key)));
          for (const row of circuit.deviceRows) {
            set.add(normalizeFilterValue(getDeviceValue(row, column.key)));
          }
        }
      }
      result[column.key] = [...set].sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
    }
    return result;
  }, [data, visibleColumns]);

  const filteredSortedSections = useMemo(() => {
    // Filtering and sorting are frontend view state. Backend sort order remains unchanged
    // until user explicitly applies sorted order.
    if (!data) {
      return [] as CircuitTreeResponseDto["sections"];
    }
    const matchesColumnFilter = (
      circuit: CircuitTreeCircuitDto,
      row: CircuitTreeDeviceRowDto | null,
      key: CellKey,
      selected: string[]
    ) => {
      if (!selected.length) {
        return true;
      }
      const values = new Set<string>();
      values.add(normalizeFilterValue(getCircuitValue(circuit, key)));
      if (row) {
        values.add(normalizeFilterValue(getDeviceValue(row, key)));
      } else {
        for (const device of circuit.deviceRows) {
          values.add(normalizeFilterValue(getDeviceValue(device, key)));
        }
      }
      return selected.some((entry) => values.has(entry));
    };

    const sections = data.sections
      .map((section) => {
        let circuits = section.circuits
          .map((circuit) => {
            const selectedFilters = Object.entries(columnFilters).filter(([, values]) => (values?.length ?? 0) > 0);
            const circuitMatchesAll = selectedFilters.every(([key, values]) =>
              matchesColumnFilter(circuit, null, key as CellKey, values ?? [])
            );
            if (!circuitMatchesAll) {
              return null;
            }

            if (!hasActiveFilters || circuit.deviceRows.length <= 1) {
              return circuit;
            }

            const matchingRows = circuit.deviceRows.filter((row) =>
              selectedFilters.every(([key, values]) =>
                matchesColumnFilter(circuit, row, key as CellKey, values ?? [])
              )
            );

            if (matchingRows.length > 0) {
              return { ...circuit, deviceRows: matchingRows };
            }
            if (selectedFilters.some(([key]) => circuitOnlyColumns.has(key as CellKey))) {
              return circuit;
            }
            return null;
          })
          .filter(Boolean) as CircuitTreeCircuitDto[];

        if (sortState) {
          // Sort on circuit blocks so multi-device circuits keep row grouping integrity.
          circuits = [...circuits].sort((a, b) => {
            const cmp = compareSortValues(getBlockSortValue(a, sortState.key), getBlockSortValue(b, sortState.key));
            return sortState.direction === "asc" ? cmp : -cmp;
          });
        }
        return { ...section, circuits };
      })
      .filter((section) => section.circuits.length > 0);

    return sections;
  }, [data, columnFilters, hasActiveFilters, sortState]);

  // visibleRows is the single normalized grid used by render + navigation + editability.
  // This avoids selected/rendered/editable state drifting apart after filters/sorts/reloads.
  const visibleRows = useMemo(() => {
    if (!filteredSortedSections.length) {
      return [] as VisibleGridRow[];
    }
    const rows: VisibleGridRow[] = [];
    for (const section of filteredSortedSections) {
      rows.push({
        rowKey: `section:${section.id}`,
        rowType: "section",
        sectionId: section.id,
        // Section headers are structural markers only: visible, but never editable/selectable.
        cells: allColumns.map((col) => ({ cellKey: col.key, editable: false, kind: "readonly", value: undefined })),
      });
      for (const circuit of section.circuits) {
        if (circuit.deviceRows.length === 0) {
          rows.push(makeRow("reserveCircuit", section.id, circuit));
          continue;
        }
        if (circuit.deviceRows.length === 1) {
          rows.push(makeRow("circuitCompact", section.id, circuit, circuit.deviceRows[0]));
          continue;
        }
        rows.push(makeRow("circuitSummary", section.id, circuit));
        for (const device of circuit.deviceRows) {
          rows.push(makeRow("deviceRow", section.id, circuit, device));
        }
      }
      // Placeholder row is virtual (-frei-) but intentionally part of editable grid because
      // editing/dropping here is the fast path to create a real circuit in this section.
      rows.push(makeRow("placeholder", section.id));
    }
    return rows;
  }, [filteredSortedSections]);

  const searchableProjectDevices = useMemo(() => {
    const term = projectDeviceSearch.trim().toLowerCase();
    if (!term) {
      return projectDevices;
    }
    return projectDevices.filter((device) => {
      const haystack = `${device.name} ${device.displayName}`.toLowerCase();
      return haystack.includes(term);
    });
  }, [projectDeviceSearch, projectDevices]);

  const circuitOptions = useMemo(() => {
    if (!data) {
      return [] as Array<{ id: string; label: string; sectionId: string }>;
    }
    return data.sections.flatMap((section) =>
      section.circuits.map((circuit) => ({
        id: circuit.id,
        sectionId: section.id,
        label: `${circuit.equipmentIdentifier} - ${circuit.displayName || "Circuit"}`,
      }))
    );
  }, [data]);

  function makeRow(
    rowType: RowType,
    sectionId: string,
    circuit?: CircuitTreeCircuitDto,
    device?: CircuitTreeDeviceRowDto
  ): VisibleGridRow {
    // Row keys are UI identities, not persisted identities. They are rebuilt on each tree refresh.
    const rowKey =
      rowType === "placeholder"
        ? `placeholder:${sectionId}`
        : rowType === "deviceRow" && device
          ? `device:${device.id}`
          : `${rowType}:${circuit?.id ?? sectionId}`;
    const cells = allColumns.map((col) => {
      const kind = getCellKind(rowType, col.key);
      // Hidden columns remain in the normalized row data; only visibility controls rendering.
      const editable = kind === "circuitField" || kind === "deviceField";
      let value: string | number | boolean | undefined;
      if (rowType === "placeholder") {
        value = col.key === "equipmentIdentifier" ? "-frei-" : undefined;
      } else if (kind === "deviceField" && device) {
        value = getDeviceValue(device, col.key);
      } else if (kind === "circuitField" && circuit) {
        value = getCircuitValue(circuit, col.key);
      } else if (col.key === "circuitTotalPower" && circuit) {
        value = circuit.circuitTotalPower;
      } else if (col.key === "rowTotalPower" && device) {
        value = device.rowTotalPower;
      }
      return { cellKey: col.key, editable, kind, value };
    });
    return { rowKey, rowType, sectionId, circuit, device, cells };
  }

  const editableCells = useMemo(
    () =>
      visibleRows.flatMap((row) =>
        row.cells
          .filter((cell) => cell.editable)
          .map((cell) => ({ rowKey: row.rowKey, cellKey: cell.cellKey as CellKey }))
      ),
    [visibleRows]
  );

  // Map each visible row to editable cells currently on screen.
  // This keeps Tab/arrow navigation aligned with hidden/reordered columns.
  const rowCellMap = useMemo(() => {
    const visibleKeySet = new Set(visibleColumnKeys);
    const map = new Map<string, CellKey[]>();
    for (const row of visibleRows) {
      const keys = row.cells
        .filter((cell) => cell.editable && visibleKeySet.has(cell.cellKey))
        .map((cell) => cell.cellKey as CellKey);
      if (keys.length) {
        map.set(row.rowKey, keys);
      }
    }
    return map;
  }, [visibleRows, visibleColumnKeys]);

  const editableRowOrder = useMemo(
    () => visibleRows.filter((row) => rowCellMap.has(row.rowKey)).map((row) => row.rowKey),
    [visibleRows, rowCellMap]
  );
  const selectableRowOrder = useMemo(
    () =>
      visibleRows
        .filter(
          (row) =>
            row.rowType === "circuitCompact" ||
            row.rowType === "circuitSummary" ||
            row.rowType === "deviceRow" ||
            row.rowType === "reserveCircuit"
        )
        .map((row) => row.rowKey),
    [visibleRows]
  );

  useEffect(() => {
    if (!selectedCell && editableCells.length > 0) {
      setSelectedCell(editableCells[0]);
      return;
    }
    if (selectedCell) {
      const valid = editableCells.some(
        (cell) => cell.rowKey === selectedCell.rowKey && cell.cellKey === selectedCell.cellKey
      );
      if (!valid) {
        setSelectedCell(editableCells[0] ?? null);
      }
    }
  }, [editableCells, selectedCell]);

  useEffect(() => {
    setSelectedRowKeys((current) => current.filter((rowKey) => selectableRowOrder.includes(rowKey)));
    setAnchorRowKey((current) => (current && selectableRowOrder.includes(current) ? current : null));
  }, [selectableRowOrder]);

  function isSelectableRowType(rowType: RowType) {
    return (
      rowType === "circuitCompact" ||
      rowType === "circuitSummary" ||
      rowType === "deviceRow" ||
      rowType === "reserveCircuit"
    );
  }

  function selectRowRange(targetRowKey: string) {
    const anchor = anchorRowKey ?? selectedRowKeys[selectedRowKeys.length - 1] ?? targetRowKey;
    const start = selectableRowOrder.indexOf(anchor);
    const end = selectableRowOrder.indexOf(targetRowKey);
    if (start < 0 || end < 0) {
      setSelectedRowKeys([targetRowKey]);
      setAnchorRowKey(targetRowKey);
      return;
    }
    const from = Math.min(start, end);
    const to = Math.max(start, end);
    setSelectedRowKeys(selectableRowOrder.slice(from, to + 1));
    setAnchorRowKey(anchor);
  }

  // Applies cell/row selection semantics. Row selection is UI-only state and later interpreted
  // as either device-row batch selection or circuit batch selection depending on drag context.
  function handleRowSelectionClick(
    row: VisibleGridRow,
    cellKey: CellKey,
    options?: { ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean }
  ) {
    if (isSelectableRowType(row.rowType)) {
      const ctrlOrMeta = Boolean(options?.ctrlKey || options?.metaKey);
      if (options?.shiftKey) {
        selectRowRange(row.rowKey);
      } else if (ctrlOrMeta) {
        setSelectedRowKeys((current) => {
          if (current.includes(row.rowKey)) {
            return current.filter((key) => key !== row.rowKey);
          }
          return [...current, row.rowKey];
        });
        setAnchorRowKey(row.rowKey);
      } else {
        setSelectedRowKeys([row.rowKey]);
        setAnchorRowKey(row.rowKey);
      }
    } else if (!options?.ctrlKey && !options?.metaKey && !options?.shiftKey) {
      setSelectedRowKeys([]);
      setAnchorRowKey(null);
    }
    setSelectedCell({ rowKey: row.rowKey, cellKey });
    requestAnimationFrame(() => containerRef.current?.focus());
  }

  // Captures semantic identity for post-reload focus restoration.
  function buildSelectionIntent(cell: SelectedCell): SelectionIntent | null {
    const row = findRow(cell.rowKey);
    if (!row) {
      return null;
    }
    return {
      rowKey: cell.rowKey,
      cellKey: cell.cellKey,
      rowType: row.rowType,
      sectionId: row.sectionId,
      circuitId: row.circuit?.id,
      deviceId: row.device?.id,
    };
  }

  // Resolves selection after writes with fallback chain:
  // exact cell -> same device -> same circuit rowType -> same circuit -> same section -> first editable.
  function resolveSelectionIntent(intent: SelectionIntent): SelectedCell | null {
    const direct = editableCells.find(
      (cell) => cell.rowKey === intent.rowKey && cell.cellKey === intent.cellKey
    );
    if (direct) {
      return direct;
    }

    const rowById = visibleRows.find((row) => {
      if (intent.deviceId && row.device?.id === intent.deviceId) {
        return true;
      }
      if (intent.circuitId && row.circuit?.id === intent.circuitId && row.rowType === intent.rowType) {
        return true;
      }
      return false;
    });
    if (rowById) {
      const rowCells = rowCellMap.get(rowById.rowKey) ?? [];
      if (rowCells.includes(intent.cellKey)) {
        return { rowKey: rowById.rowKey, cellKey: intent.cellKey };
      }
      if (rowCells.length > 0) {
        return { rowKey: rowById.rowKey, cellKey: rowCells[0] };
      }
    }

    const inSameCircuit = visibleRows.find((row) => intent.circuitId && row.circuit?.id === intent.circuitId);
    if (inSameCircuit) {
      const cells = rowCellMap.get(inSameCircuit.rowKey) ?? [];
      if (cells.length > 0) {
        return { rowKey: inSameCircuit.rowKey, cellKey: cells[0] };
      }
    }

    const inSameSection = visibleRows.find((row) => row.sectionId === intent.sectionId && rowCellMap.has(row.rowKey));
    if (inSameSection) {
      const cells = rowCellMap.get(inSameSection.rowKey) ?? [];
      if (cells.length > 0) {
        return { rowKey: inSameSection.rowKey, cellKey: cells[0] };
      }
    }

    return editableCells[0] ?? null;
  }

  // Reload helper for write paths. Stores pending intent because row/cell keys can change after reload.
  async function reloadWithIntent(intent?: SelectionIntent | null) {
    if (intent) {
      pendingSelectionAfterReload.current = intent;
    }
    await loadTree({ showLoading: false });
    if (!intent) {
      requestAnimationFrame(() => containerRef.current?.focus());
    }
  }

  // Runs a normal command and records it in session-local history.
  async function runCommand(command: HistoryCommand) {
    try {
      setError(null);
      setIsSaving(true);
      const intent = await command.redo();
      await reloadWithIntent(intent ?? null);
      // Any new forward command invalidates redo history branch.
      setUndoStack((current) => [...current, command]);
      setRedoStack([]);
    } catch (err) {
      setError(normalizeUiError(err));
    } finally {
      setIsSaving(false);
    }
  }

  // Replays command in undo/redo mode. Commands encapsulate their own inverse logic.
  async function applyHistory(command: HistoryCommand, mode: "undo" | "redo") {
    try {
      setError(null);
      setHistoryBusy(true);
      setIsSaving(true);
      const intent = mode === "undo" ? await command.undo() : await command.redo();
      await reloadWithIntent(intent ?? null);
      if (mode === "undo") {
        setUndoStack((current) => current.slice(0, -1));
        setRedoStack((current) => [...current, command]);
      } else {
        setRedoStack((current) => current.slice(0, -1));
        setUndoStack((current) => [...current, command]);
      }
    } catch (err) {
      setError(normalizeUiError(err));
    } finally {
      setIsSaving(false);
      setHistoryBusy(false);
    }
  }

  // Undo command from session-local history stack.
  async function handleUndo() {
    if (historyBusy || isSaving || undoStack.length === 0) {
      return;
    }
    const command = undoStack[undoStack.length - 1];
    await applyHistory(command, "undo");
  }

  // Redo command from session-local history stack.
  async function handleRedo() {
    if (historyBusy || isSaving || redoStack.length === 0) {
      return;
    }
    const command = redoStack[redoStack.length - 1];
    await applyHistory(command, "redo");
  }

  // Persists currently sorted block order into section sortOrder values.
  async function handleApplySortedOrder() {
    if (!sortState || hasActiveFilters || !data) {
      return;
    }

    const beforeBySection = data.sections.map((section) => ({
      sectionId: section.id,
      order: section.circuits.map((circuit) => circuit.id),
    }));
    const afterBySection = filteredSortedSections.map((section) => ({
      sectionId: section.id,
      order: section.circuits.map((circuit) => circuit.id),
    }));

    const changedSections = afterBySection.filter((after) => {
      const before = beforeBySection.find((entry) => entry.sectionId === after.sectionId);
      if (!before) {
        return false;
      }
      if (before.order.length !== after.order.length) {
        return false;
      }
      return before.order.some((id, index) => id !== after.order[index]);
    });

    if (changedSections.length === 0) {
      setSortState(null);
      return;
    }

    // Sorting is view-only until explicitly applied; this avoids accidental persisted reorders.
    await runCommand({
      label: "Apply sorted order",
      redo: async () => {
        for (const section of changedSections) {
          await reorderSectionCircuits(section.sectionId, section.order);
        }
        setSortState(null);
        setOpenFilterColumn(null);
        return null;
      },
      undo: async () => {
        for (const section of beforeBySection) {
          await reorderSectionCircuits(section.sectionId, section.order);
        }
        return null;
      },
    });
  }

  // Toggles visible columns while keeping full column model intact for data/edit mapping.
  function toggleColumnVisibility(key: CellKey) {
    const column = allColumns.find((entry) => entry.key === key);
    if (!column || column.locked) {
      return;
    }
    setVisibleColumnKeys((current) => {
      if (current.includes(key)) {
        return current.filter((entry) => entry !== key);
      }
      return [...current, key];
    });
  }

  function moveColumn(key: CellKey, direction: -1 | 1) {
    if (key === "equipmentIdentifier") {
      return;
    }
    setColumnOrder((current) => {
      const index = current.indexOf(key);
      if (index < 0) {
        return current;
      }
      const nextIndex = index + direction;
      if (nextIndex <= 0 || nextIndex >= current.length) {
        return current;
      }
      const clone = [...current];
      const [item] = clone.splice(index, 1);
      clone.splice(nextIndex, 0, item);
      return normalizeColumnOrder(clone);
    });
  }

  function resetColumns() {
    setVisibleColumnKeys(defaultVisibleColumnKeys);
    setColumnOrder(normalizeColumnOrder(allColumns.map((column) => column.key)));
    setOpenFilterColumn(null);
  }

  // Column drag affects layout state only and never mutates circuit/device data.
  function moveColumnByDrag(dragKey: CellKey, targetKey: CellKey) {
    if (dragKey === "equipmentIdentifier" || targetKey === "equipmentIdentifier") {
      return;
    }
    setColumnOrder((current) => {
      const from = current.indexOf(dragKey);
      const to = current.indexOf(targetKey);
      if (from < 1 || to < 1 || from === to) {
        return current;
      }
      const clone = [...current];
      const [item] = clone.splice(from, 1);
      const targetIndex = clone.indexOf(targetKey);
      if (targetIndex < 1) {
        return current;
      }
      clone.splice(targetIndex, 0, item);
      return normalizeColumnOrder(clone);
    });
  }

  function handleColumnDragStart(event: DragEvent<HTMLDivElement>, key: CellKey) {
    if (key === "equipmentIdentifier") {
      event.preventDefault();
      return;
    }
    setDraggingColumnKey(key);
    setColumnDropTargetKey(null);
    event.dataTransfer.effectAllowed = "move";
    event.dataTransfer.setData("application/x-column-key", key);
  }

  function handleColumnDragOver(event: DragEvent<HTMLDivElement>, targetKey: CellKey) {
    if (!draggingColumnKey || targetKey === "equipmentIdentifier" || draggingColumnKey === targetKey) {
      return;
    }
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
    setColumnDropTargetKey(targetKey);
  }

  function handleColumnDrop(event: DragEvent<HTMLDivElement>, targetKey: CellKey) {
    event.preventDefault();
    const dragKey = draggingColumnKey;
    if (!dragKey || dragKey === targetKey) {
      setColumnDropTargetKey(null);
      return;
    }
    moveColumnByDrag(dragKey, targetKey);
    setDraggingColumnKey(null);
    setColumnDropTargetKey(null);
  }

  function handleColumnDragEnd() {
    setDraggingColumnKey(null);
    setColumnDropTargetKey(null);
  }

  // Apply deferred selection after async reload only once visible grid is rebuilt.
  useEffect(() => {
    const intent = pendingSelectionAfterReload.current;
    if (!intent || !data) {
      return;
    }
    const resolved = resolveSelectionIntent(intent);
    pendingSelectionAfterReload.current = null;
    if (resolved) {
      setSelectedCell(resolved);
    }
    requestAnimationFrame(() => containerRef.current?.focus());
  }, [data, editableCells, visibleRows]);

  // Restores multi-selected device rows after operations that rebuild row keys.
  useEffect(() => {
    const pending = pendingSelectedDeviceRowIdsAfterReload.current;
    if (!pending || pending.length === 0) {
      return;
    }
    const rowKeys: string[] = [];
    for (const row of visibleRows) {
      if ((row.rowType === "deviceRow" || row.rowType === "circuitCompact") && row.device?.id && pending.includes(row.device.id)) {
        rowKeys.push(row.rowKey);
      }
    }
    if (rowKeys.length > 0) {
      setSelectedRowKeys(rowKeys);
      setAnchorRowKey(rowKeys[0]);
    }
    pendingSelectedDeviceRowIdsAfterReload.current = null;
  }, [visibleRows]);

  // Restores multi-selected circuits after reorder/delete/recreate operations.
  useEffect(() => {
    const pending = pendingSelectedCircuitIdsAfterReload.current;
    if (!pending || pending.length === 0) {
      return;
    }
    const rowKeys: string[] = [];
    for (const row of visibleRows) {
      if (
        (row.rowType === "circuitCompact" || row.rowType === "circuitSummary" || row.rowType === "reserveCircuit") &&
        row.circuit?.id &&
        pending.includes(row.circuit.id)
      ) {
        rowKeys.push(row.rowKey);
      }
    }
    if (rowKeys.length > 0) {
      setSelectedRowKeys(rowKeys);
      setAnchorRowKey(rowKeys[0]);
    }
    pendingSelectedCircuitIdsAfterReload.current = null;
  }, [visibleRows]);

  // PendingFocus is used by toolbar actions that should immediately enter edit mode on a target cell.
  useEffect(() => {
    if (!pendingFocus) {
      return;
    }
    setSelectedCell(pendingFocus);
    startEdit(pendingFocus, "selectExisting");
    setPendingFocus(null);
  }, [pendingFocus]);

  useEffect(() => {
    if (!isColumnMenuOpen) {
      setDraggingColumnKey(null);
      setColumnDropTargetKey(null);
    }
  }, [isColumnMenuOpen]);

  useEffect(() => {
    if (!editingCell) {
      return;
    }
    requestAnimationFrame(() => {
      if (!inputRef.current) {
        return;
      }
      inputRef.current.focus();
      if (editingCell.mode === "selectExisting") {
        inputRef.current.select();
      } else {
        const len = inputRef.current.value.length;
        inputRef.current.setSelectionRange(len, len);
      }
    });
  }, [editingCell?.focusToken]);

  // Finds row in normalized grid by virtual row key.
  function findRow(rowKey: string) {
    return visibleRows.find((row) => row.rowKey === rowKey);
  }

  // Finds cell in normalized grid row.
  function findCell(rowKey: string, cellKey: CellKey) {
    const row = findRow(rowKey);
    return row?.cells.find((cell) => cell.cellKey === cellKey);
  }

  // Opens edit session for selected cell.
  // Enter/F2/double-click use selectExisting; type-to-edit uses replaceWithTypedChar.
  function startEdit(cell: SelectedCell, mode: StartEditMode, typedChar?: string) {
    const visibleCell = findCell(cell.rowKey, cell.cellKey);
    if (!visibleCell || !visibleCell.editable) {
      return;
    }
    // Spreadsheet behavior: typing on a selected cell starts overwrite mode.
    const currentDisplay = mode === "replaceWithTypedChar" ? typedChar ?? "" : String(visibleCell.value ?? "");
    focusTokenRef.current += 1;
    setEditingCell({
      ...cell,
      mode,
      draft: currentDisplay === "-" ? "" : currentDisplay,
      focusToken: focusTokenRef.current,
    });
  }

  // Horizontal keyboard navigation constrained to editable cells in current row.
  function moveHorizontal(direction: 1 | -1) {
    if (!selectedCell) {
      return;
    }
    const rowCells = rowCellMap.get(selectedCell.rowKey) ?? [];
    const idx = rowCells.indexOf(selectedCell.cellKey);
    if (idx < 0) {
      return;
    }
    const next = rowCells[Math.min(rowCells.length - 1, Math.max(0, idx + direction))];
    setSelectedCell({ rowKey: selectedCell.rowKey, cellKey: next });
  }

  // Vertical keyboard navigation preserves nearest visible column when row cell sets differ.
  function moveVertical(direction: 1 | -1) {
    if (!selectedCell) {
      return;
    }
    const rowIndex = editableRowOrder.indexOf(selectedCell.rowKey);
    if (rowIndex < 0) {
      return;
    }
    const targetIndex = rowIndex + direction;
    if (targetIndex < 0 || targetIndex >= editableRowOrder.length) {
      return;
    }
    const targetRowKey = editableRowOrder[targetIndex];
    const targetCells = rowCellMap.get(targetRowKey) ?? [];
    if (!targetCells.length) {
      return;
    }
    const preferred = visibleColumns.findIndex((col) => col.key === selectedCell.cellKey);
    let best = targetCells[0];
    let bestDistance = Number.POSITIVE_INFINITY;
    for (const key of targetCells) {
      const idx = visibleColumns.findIndex((col) => col.key === key);
      const dist = Math.abs(idx - preferred);
      if (dist < bestDistance) {
        bestDistance = dist;
        best = key;
      }
    }
    setSelectedCell({ rowKey: targetRowKey, cellKey: best });
  }

  // Maps generic grid cell edits to circuit PATCH payload fields.
  async function patchCircuit(circuitId: string, key: CellKey, draft: string) {
    const payload: Record<string, unknown> = {};
    if (key === "protectionSummary" || key === "cableSummary") {
      payload.remark = draft.trim() === "" ? undefined : draft;
    } else if (["protectionRatedCurrent", "cableLength"].includes(key)) {
      payload[key] = parseNumeric(key, draft);
    } else if (key === "isReserve") {
      const normalized = draft.trim().toLowerCase();
      payload.isReserve = ["1", "true", "yes", "ja"].includes(normalized);
    } else {
      payload[key] = draft.trim() === "" ? undefined : draft;
    }
    await updateCircuitById(circuitId, payload);
  }

  // Maps generic grid cell edits to device-row PATCH payload fields.
  async function patchDeviceRow(rowId: string, key: CellKey, draft: string) {
    const payload: Record<string, unknown> = {};
    if (["quantity", "powerPerUnit", "simultaneityFactor", "cosPhi"].includes(key)) {
      payload[key] = parseNumeric(key, draft);
    } else if (key === "roomSummary") {
      const trimmed = draft.trim();
      if (!trimmed) {
        payload.roomNumberSnapshot = undefined;
        payload.roomNameSnapshot = undefined;
      } else {
        const firstSpace = trimmed.indexOf(" ");
        if (firstSpace > 0) {
          payload.roomNumberSnapshot = trimmed.slice(0, firstSpace).trim();
          payload.roomNameSnapshot = trimmed.slice(firstSpace + 1).trim();
        } else {
          payload.roomNumberSnapshot = trimmed;
          payload.roomNameSnapshot = undefined;
        }
      }
    } else {
      payload[key] = draft.trim() === "" ? undefined : draft;
    }
    await updateCircuitDeviceRowById(rowId, payload);
  }

  // Creates a real circuit from virtual placeholder row.
  // Device-field edits create circuit + first manual row; circuit-field edits patch circuit directly.
  async function createFromPlaceholder(
    sectionId: string,
    key: CellKey,
    draft: string
  ): Promise<SelectedCell> {
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      throw new Error("Section not found.");
    }
    const next = await getNextCircuitIdentifier(sectionId);
    const sortOrder =
      section.circuits.length > 0 ? Math.max(...section.circuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
    const isDeviceField = deviceFieldKeys.has(key);
    const createdCircuit = (await createCircuit(projectId, circuitListId, {
      sectionId,
      equipmentIdentifier: next.nextIdentifier,
      displayName: "New circuit",
      sortOrder,
      isReserve: !isDeviceField,
    })) as CircuitTreeCircuitDto;

    if (isDeviceField) {
      const createdRow = (await createCircuitDeviceRow(createdCircuit.id, {
        name: "Manual device",
        displayName: "Manual device",
        phaseType: "single_phase",
        quantity: 1,
        powerPerUnit: 0,
        simultaneityFactor: 1,
        cosPhi: 1,
      })) as { id: string };
      await patchDeviceRow(createdRow.id, key, draft);
      return { rowKey: `circuitCompact:${createdCircuit.id}`, cellKey: key };
    }

    await patchCircuit(createdCircuit.id, key, draft);
    return { rowKey: `reserveCircuit:${createdCircuit.id}`, cellKey: key };
  }

  // Reads current persisted value into editable draft text for reversible edit commands.
  function getCurrentDraftForCell(row: VisibleGridRow, cellKey: CellKey, kind: CellKind) {
    if (kind === "deviceField" && row.device) {
      return String(getDeviceValue(row.device, cellKey) ?? "");
    }
    if (kind === "circuitField" && row.circuit) {
      return String(getCircuitValue(row.circuit, cellKey) ?? "");
    }
    return "";
  }

  // Commits the active editor input through command history so edits remain undoable.
  async function commitEdit(direction: SaveDirection = "stay") {
    if (!editingCell) {
      return;
    }
    const row = findRow(editingCell.rowKey);
    const cell = row?.cells.find((entry) => entry.cellKey === editingCell.cellKey);
    if (!row || !cell || !cell.editable) {
      setEditingCell(null);
      return;
    }

    let targetSelection: SelectedCell = { rowKey: editingCell.rowKey, cellKey: editingCell.cellKey };
    const nextSelectionIntent = () => {
      let selected = targetSelection;
      if (direction !== "stay") {
        const idx = editableCells.findIndex(
          (entry) => entry.rowKey === selected.rowKey && entry.cellKey === selected.cellKey
        );
        if (idx >= 0) {
          const targetIdx =
            direction === "next" ? Math.min(editableCells.length - 1, idx + 1) : Math.max(0, idx - 1);
          selected = editableCells[targetIdx];
        }
      }
      return buildSelectionIntent(selected);
    };

    // Placeholder rows are not persisted entities; editing them materializes a real circuit first.
    if (row.rowType === "placeholder") {
      let createdCircuitId: string | null = null;
      const command: HistoryCommand = {
        label: "Create from placeholder",
        redo: async () => {
          const selection = await createFromPlaceholder(row.sectionId, editingCell.cellKey, editingCell.draft);
          targetSelection = selection;
          const createdRow = selection.rowKey.startsWith("circuitCompact:")
            ? selection.rowKey.replace("circuitCompact:", "")
            : selection.rowKey.replace("reserveCircuit:", "");
          createdCircuitId = createdRow;
          return nextSelectionIntent();
        },
        undo: async () => {
          if (!createdCircuitId) {
            return null;
          }
          await deleteCircuitById(createdCircuitId);
          return buildSelectionIntent({ rowKey: `placeholder:${row.sectionId}`, cellKey: editingCell.cellKey });
        },
      };
      setEditingCell(null);
      await runCommand(command);
      return;
    }

    if (cell.kind === "circuitField" && row.circuit) {
      const previous = getCurrentDraftForCell(row, editingCell.cellKey, cell.kind);
      const next = editingCell.draft;
      const command: HistoryCommand = {
        label: "Edit circuit field",
        redo: async () => {
          await patchCircuit(row.circuit!.id, editingCell.cellKey, next);
          return nextSelectionIntent();
        },
        undo: async () => {
          await patchCircuit(row.circuit!.id, editingCell.cellKey, previous);
          return buildSelectionIntent({ rowKey: row.rowKey, cellKey: editingCell.cellKey });
        },
      };
      setEditingCell(null);
      await runCommand(command);
      return;
    }

    if (cell.kind === "deviceField" && row.device) {
      const previous = getCurrentDraftForCell(row, editingCell.cellKey, cell.kind);
      const next = editingCell.draft;
      const command: HistoryCommand = {
        label: "Edit device field",
        redo: async () => {
          await patchDeviceRow(row.device!.id, editingCell.cellKey, next);
          return nextSelectionIntent();
        },
        undo: async () => {
          await patchDeviceRow(row.device!.id, editingCell.cellKey, previous);
          return buildSelectionIntent({ rowKey: `device:${row.device!.id}`, cellKey: editingCell.cellKey });
        },
      };
      setEditingCell(null);
      await runCommand(command);
      return;
    }

    if (cell.kind === "deviceField" && row.rowType === "reserveCircuit" && row.circuit) {
      const next = editingCell.draft;
      let createdRowId: string | null = null;
      const command: HistoryCommand = {
        label: "Create reserve device row",
        redo: async () => {
          const created = (await createCircuitDeviceRow(row.circuit!.id, {
            name: "Reserve load",
            displayName: "Reserve load",
            phaseType: "single_phase",
            quantity: 1,
            powerPerUnit: 0,
            simultaneityFactor: 1,
            cosPhi: 1,
          })) as { id: string };
          createdRowId = created.id;
          await patchDeviceRow(created.id, editingCell.cellKey, next);
          targetSelection = { rowKey: `circuitCompact:${row.circuit!.id}`, cellKey: editingCell.cellKey };
          return nextSelectionIntent();
        },
        undo: async () => {
          if (!createdRowId) {
            return null;
          }
          await deleteCircuitDeviceRowById(createdRowId);
          return buildSelectionIntent({ rowKey: `reserveCircuit:${row.circuit!.id}`, cellKey: editingCell.cellKey });
        },
      };
      setEditingCell(null);
      await runCommand(command);
    }
  }

  // Cancels current edit session and restores grid keyboard focus.
  function cancelEdit() {
    if (!editingCell) {
      return;
    }
    setEditingCell(null);
    setSelectedCell({ rowKey: editingCell.rowKey, cellKey: editingCell.cellKey });
    requestAnimationFrame(() => containerRef.current?.focus());
  }

  async function handleAddReserveCircuit(sectionId: string) {
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      return;
    }
    let createdCircuitId: string | null = null;
    await runCommand({
      label: "Add circuit",
      redo: async () => {
        const next = await getNextCircuitIdentifier(sectionId);
        const sortOrder =
          section.circuits.length > 0 ? Math.max(...section.circuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
        const created = (await createCircuit(projectId, circuitListId, {
          sectionId,
          equipmentIdentifier: next.nextIdentifier,
          displayName: "Reserve",
          sortOrder,
          isReserve: true,
        })) as CircuitTreeCircuitDto;
        createdCircuitId = created.id;
        setActiveSectionId(sectionId);
        return {
          rowKey: `reserveCircuit:${created.id}`,
          cellKey: "equipmentIdentifier",
          rowType: "reserveCircuit",
          sectionId,
          circuitId: created.id,
        };
      },
      undo: async () => {
        if (createdCircuitId) {
          await deleteCircuitById(createdCircuitId);
        }
        return {
          rowKey: `placeholder:${sectionId}`,
          cellKey: "equipmentIdentifier",
          rowType: "placeholder",
          sectionId,
        };
      },
    });
  }

  async function handleAddManualDevice(circuit: CircuitTreeCircuitDto, sectionId: string) {
    let createdRowId: string | null = null;
    await runCommand({
      label: "Add manual device",
      redo: async () => {
        const created = (await createCircuitDeviceRow(circuit.id, {
          name: "Manual device",
          displayName: "Manual device",
          phaseType: "single_phase",
          quantity: 1,
          powerPerUnit: 0,
          simultaneityFactor: 1,
          cosPhi: 1,
        })) as { id: string };
        createdRowId = created.id;
        setActiveSectionId(sectionId);
        return {
          rowKey: `device:${created.id}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId,
          circuitId: circuit.id,
          deviceId: created.id,
        };
      },
      undo: async () => {
        if (createdRowId) {
          await deleteCircuitDeviceRowById(createdRowId);
        }
        return {
          rowKey: `reserveCircuit:${circuit.id}`,
          cellKey: "displayName",
          rowType: "reserveCircuit",
          sectionId,
          circuitId: circuit.id,
        };
      },
    });
  }

  function resolvePhaseType(device: ProjectDeviceDto): string {
    return device.phaseType;
  }

  function resolveSelectedProjectDevice() {
    if (!selectedProjectDeviceId) {
      return null;
    }
    return projectDevices.find((device) => device.id === selectedProjectDeviceId) ?? null;
  }

  async function handleAddProjectDeviceAsNewCircuit() {
    const device = resolveSelectedProjectDevice();
    if (!device) {
      setError("Please select a project device.");
      return;
    }
    if (!targetSectionId) {
      setError("Please select a target section.");
      return;
    }
    let createdCircuitId: string | null = null;
    let createdRowId: string | null = null;
    await runCommand({
      label: "Add project device as new circuit",
      redo: async () => {
        const created = await insertProjectDeviceAsNewCircuit(device, targetSectionId);
        createdCircuitId = created.circuitId;
        createdRowId = created.rowId;
        return {
          rowKey: `device:${created.rowId}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId: targetSectionId,
          circuitId: created.circuitId,
          deviceId: created.rowId,
        };
      },
      undo: async () => {
        if (createdCircuitId) {
          await deleteCircuitById(createdCircuitId);
        }
        return {
          rowKey: `placeholder:${targetSectionId}`,
          cellKey: "displayName",
          rowType: "placeholder",
          sectionId: targetSectionId,
        };
      },
    });
  }

  async function handleAddProjectDeviceToCircuit() {
    const device = resolveSelectedProjectDevice();
    if (!device) {
      setError("Please select a project device.");
      return;
    }
    let circuitId = targetCircuitId;
    if (!circuitId && selectedCell) {
      const row = findRow(selectedCell.rowKey);
      if (row?.circuit?.id) {
        circuitId = row.circuit.id;
      }
    }
    if (!circuitId) {
      setError("Please select a target circuit.");
      return;
    }

    let createdRowId: string | null = null;
    await runCommand({
      label: "Add project device to circuit",
      redo: async () => {
        const created = await insertProjectDeviceToCircuit(device, circuitId);
        createdRowId = created.rowId;
        return {
          rowKey: `device:${created.rowId}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId: findCircuitSectionId(circuitId) ?? "",
          circuitId,
          deviceId: created.rowId,
        };
      },
      undo: async () => {
        if (createdRowId) {
          await deleteCircuitDeviceRowById(createdRowId);
        }
        return {
          rowKey: `circuitSummary:${circuitId}`,
          cellKey: "displayName",
          rowType: "circuitSummary",
          sectionId: findCircuitSectionId(circuitId) ?? "",
          circuitId,
        };
      },
    });
  }

  async function insertProjectDeviceAsNewCircuit(device: ProjectDeviceDto, sectionId: string) {
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      throw new Error("Invalid target section.");
    }
    const next = await getNextCircuitIdentifier(sectionId);
    const sortOrder =
      section.circuits.length > 0 ? Math.max(...section.circuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
    const createdCircuit = (await createCircuit(projectId, circuitListId, {
      sectionId,
      equipmentIdentifier: next.nextIdentifier,
      displayName: device.displayName || device.name,
      sortOrder,
      isReserve: false,
    })) as CircuitTreeCircuitDto;

    const createdRow = (await createCircuitDeviceRow(createdCircuit.id, {
      linkedProjectDeviceId: device.id,
      name: device.name,
      displayName: device.displayName,
      phaseType: resolvePhaseType(device),
      connectionKind: device.connectionKind ?? undefined,
      costGroup: device.costGroup ?? undefined,
      quantity: device.quantity,
      powerPerUnit: device.powerPerUnit,
      simultaneityFactor: device.simultaneityFactor,
      cosPhi: device.cosPhi ?? undefined,
      category: device.category ?? undefined,
      remark: device.remark ?? undefined,
    })) as { id: string };

    setActiveSectionId(sectionId);
    setTargetCircuitId(createdCircuit.id);
    return { circuitId: createdCircuit.id, rowId: createdRow.id };
  }

  async function insertProjectDeviceToCircuit(device: ProjectDeviceDto, circuitId: string) {
    const createdRow = (await createCircuitDeviceRow(circuitId, {
      linkedProjectDeviceId: device.id,
      name: device.name,
      displayName: device.displayName,
      phaseType: resolvePhaseType(device),
      connectionKind: device.connectionKind ?? undefined,
      costGroup: device.costGroup ?? undefined,
      quantity: device.quantity,
      powerPerUnit: device.powerPerUnit,
      simultaneityFactor: device.simultaneityFactor,
      cosPhi: device.cosPhi ?? undefined,
      category: device.category ?? undefined,
      remark: device.remark ?? undefined,
    })) as { id: string };
    return { rowId: createdRow.id };
  }

  function getCircuitSnapshot(circuitId: string): CircuitSnapshot | null {
    for (const section of data?.sections ?? []) {
      const found = section.circuits.find((entry) => entry.id === circuitId);
      if (!found) {
        continue;
      }
      return {
        id: found.id,
        sectionId: found.sectionId,
        equipmentIdentifier: found.equipmentIdentifier,
        displayName: found.displayName ?? undefined,
        sortOrder: found.sortOrder,
        protectionType: found.protectionType ?? undefined,
        protectionRatedCurrent: found.protectionRatedCurrent ?? undefined,
        protectionCharacteristic: found.protectionCharacteristic ?? undefined,
        cableType: found.cableType ?? undefined,
        cableCrossSection: found.cableCrossSection ?? undefined,
        cableLength: found.cableLength ?? undefined,
        rcdAssignment: found.rcdAssignment ?? undefined,
        terminalDesignation: found.terminalDesignation ?? undefined,
        voltage: found.voltage ?? undefined,
        status: found.status ?? undefined,
        isReserve: found.isReserve,
        remark: found.remark ?? undefined,
        deviceRows: found.deviceRows.map((row) => ({ ...row })),
      };
    }
    return null;
  }

  async function recreateCircuit(snapshot: CircuitSnapshot) {
    const createdCircuit = (await createCircuit(projectId, circuitListId, {
      sectionId: snapshot.sectionId,
      equipmentIdentifier: snapshot.equipmentIdentifier,
      displayName: snapshot.displayName,
      sortOrder: snapshot.sortOrder,
      protectionType: snapshot.protectionType,
      protectionRatedCurrent: snapshot.protectionRatedCurrent,
      protectionCharacteristic: snapshot.protectionCharacteristic,
      cableType: snapshot.cableType,
      cableCrossSection: snapshot.cableCrossSection,
      cableLength: snapshot.cableLength,
      rcdAssignment: snapshot.rcdAssignment,
      terminalDesignation: snapshot.terminalDesignation,
      voltage: snapshot.voltage,
      status: snapshot.status,
      isReserve: snapshot.isReserve,
      remark: snapshot.remark,
    })) as CircuitTreeCircuitDto;

    const createdRowIds: string[] = [];
    for (const row of snapshot.deviceRows) {
      const created = (await createCircuitDeviceRow(createdCircuit.id, {
        linkedProjectDeviceId: row.linkedProjectDeviceId,
        name: row.name,
        displayName: row.displayName,
        phaseType: row.phaseType,
        connectionKind: row.connectionKind,
        costGroup: row.costGroup,
        category: row.category,
        level: row.level,
        roomId: row.roomId,
        roomNumberSnapshot: row.roomNumberSnapshot,
        roomNameSnapshot: row.roomNameSnapshot,
        quantity: row.quantity,
        powerPerUnit: row.powerPerUnit,
        simultaneityFactor: row.simultaneityFactor,
        cosPhi: row.cosPhi,
        remark: row.remark,
        overriddenFields: row.overriddenFields,
        sortOrder: row.sortOrder,
      })) as { id: string };
      createdRowIds.push(created.id);
    }
    return { circuitId: createdCircuit.id, rowIds: createdRowIds };
  }

  function parseDraggedProjectDeviceId(event: DragEvent<HTMLElement>) {
    return event.dataTransfer.getData("application/x-project-device-id") || null;
  }

  function parseDraggedDeviceRowId(event: DragEvent<HTMLElement>) {
    return event.dataTransfer.getData("application/x-circuit-device-row-id") || null;
  }

  function parseDraggedDeviceRowIds(event: DragEvent<HTMLElement>) {
    const raw = event.dataTransfer.getData("application/x-circuit-device-row-ids");
    if (!raw) {
      return [];
    }
    try {
      const parsed = JSON.parse(raw) as unknown;
      if (!Array.isArray(parsed)) {
        return [];
      }
      return parsed.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
    } catch {
      return [];
    }
  }

  function parseDraggedCircuitIds(event: DragEvent<HTMLElement>) {
    const raw = event.dataTransfer.getData("application/x-circuit-ids");
    if (!raw) {
      return [];
    }
    try {
      const parsed = JSON.parse(raw) as unknown;
      if (!Array.isArray(parsed)) {
        return [];
      }
      return parsed.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
    } catch {
      return [];
    }
  }

  function findDeviceRowCircuitId(deviceRowId: string) {
    for (const section of data?.sections ?? []) {
      for (const circuit of section.circuits) {
        if (circuit.deviceRows.some((row) => row.id === deviceRowId)) {
          return circuit.id;
        }
      }
    }
    return null;
  }

  // Converts current row selection to device-row ids for bulk row moves.
  // Section and placeholder rows are intentionally ignored.
  function getSelectedEligibleDeviceRowIds(primaryRowId?: string | null) {
    const selectedSet = new Set(selectedRowKeys);
    const ids: string[] = [];
    for (const row of visibleRows) {
      if (!selectedSet.has(row.rowKey)) {
        continue;
      }
      if ((row.rowType === "deviceRow" || row.rowType === "circuitCompact") && row.device?.id) {
        ids.push(row.device.id);
      }
    }
    if (ids.length > 1 && primaryRowId && ids.includes(primaryRowId)) {
      return ids;
    }
    return primaryRowId ? [primaryRowId] : ids;
  }

  // Converts current row selection to unique circuit ids for circuit block reorder.
  function getSelectedEligibleCircuitIds(primaryCircuitId?: string | null) {
    const selectedSet = new Set(selectedRowKeys);
    const ids: string[] = [];
    const seen = new Set<string>();
    for (const row of visibleRows) {
      if (!selectedSet.has(row.rowKey)) {
        continue;
      }
      if (
        (row.rowType === "circuitCompact" || row.rowType === "circuitSummary" || row.rowType === "reserveCircuit") &&
        row.circuit?.id &&
        !seen.has(row.circuit.id)
      ) {
        seen.add(row.circuit.id);
        ids.push(row.circuit.id);
      }
    }
    if (ids.length > 1 && primaryCircuitId && ids.includes(primaryCircuitId)) {
      return ids;
    }
    return primaryCircuitId ? [primaryCircuitId] : ids;
  }

  function findCircuitSectionId(circuitId: string) {
    for (const section of data?.sections ?? []) {
      if (section.circuits.some((circuit) => circuit.id === circuitId)) {
        return section.id;
      }
    }
    return null;
  }

  // Applies same-section circuit reorder as explicit id ordering.
  // No implicit renumbering is performed here.
  async function applyCircuitReorder(intent: CircuitReorderDropIntent, sourceCircuitIds: string[]) {
    const section = data?.sections.find((entry) => entry.id === intent.sectionId);
    if (!section) {
      throw new Error("Invalid section id.");
    }
    const ids = section.circuits.map((circuit) => circuit.id);
    const block = sourceCircuitIds.filter((id) => ids.includes(id));
    if (block.length === 0) {
      throw new Error("Invalid source circuit.");
    }
    const blockSet = new Set(block);
    const nextIds = ids.filter((id) => !blockSet.has(id));

    if (intent.kind === "section-end") {
      nextIds.push(...block);
    } else {
      const targetIndex = nextIds.indexOf(intent.targetCircuitId);
      if (targetIndex < 0) {
        throw new Error("Invalid target circuit.");
      }
      const insertIndex = intent.kind === "after-circuit" ? targetIndex + 1 : targetIndex;
      nextIds.splice(insertIndex, 0, ...block);
    }
    await reorderSectionCircuits(section.id, nextIds);
  }

  // Handles project-device drag intent. This path creates linked rows/circuits and
  // must remain separate from move handlers that operate on existing entities.
  async function handleDropWithIntent(event: DragEvent<HTMLElement>, intent: ProjectDeviceDropIntent) {
    event.preventDefault();
    event.stopPropagation();
    const draggedId = parseDraggedProjectDeviceId(event) ?? draggingProjectDeviceId;
    setDropIntent(null);
    setDraggingProjectDeviceId(null);
    setDraggingDeviceRowIds([]);
    setDraggingCircuitIds([]);
    if (!draggedId) {
      setError("Missing dragged project device.");
      return;
    }
    const device = projectDevices.find((entry) => entry.id === draggedId);
    if (!device) {
      setError("Invalid project device drop source.");
      return;
    }
    // Project-device drop logic is isolated from row/circuit move logic because
    // it can create new rows/circuits instead of moving existing ones.
    if (intent.kind === "new-circuit") {
      let createdCircuitId: string | null = null;
      await runCommand({
        label: "Drop project device to new circuit",
        redo: async () => {
          const created = await insertProjectDeviceAsNewCircuit(device, intent.sectionId);
          createdCircuitId = created.circuitId;
          return {
            rowKey: `device:${created.rowId}`,
            cellKey: "displayName",
            rowType: "deviceRow",
            sectionId: intent.sectionId,
            circuitId: created.circuitId,
            deviceId: created.rowId,
          };
        },
        undo: async () => {
          if (createdCircuitId) {
            await deleteCircuitById(createdCircuitId);
          }
          return null;
        },
      });
      return;
    }
    let createdRowId: string | null = null;
    await runCommand({
      label: "Drop project device to circuit",
      redo: async () => {
        const created = await insertProjectDeviceToCircuit(device, intent.circuitId);
        createdRowId = created.rowId;
        return {
          rowKey: `device:${created.rowId}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId: intent.sectionId,
          circuitId: intent.circuitId,
          deviceId: created.rowId,
        };
      },
      undo: async () => {
        if (createdRowId) {
          await deleteCircuitDeviceRowById(createdRowId);
        }
        return null;
      },
    });
  }

  // Handles device-row drag intent (single or bulk) and preserves enough source grouping
  // for deterministic undo back to original circuits.
  async function handleDeviceRowDropWithIntent(event: DragEvent<HTMLElement>, intent: DeviceRowMoveDropIntent) {
    event.preventDefault();
    event.stopPropagation();
    const draggedRowIds = parseDraggedDeviceRowIds(event);
    const singleRowId = parseDraggedDeviceRowId(event) ?? draggingDeviceRowId;
    const rowIds = draggedRowIds.length > 0 ? draggedRowIds : singleRowId ? [singleRowId] : [];
    setDeviceMoveIntent(null);
    setDraggingDeviceRowId(null);
    setDraggingDeviceRowIds([]);
    setDraggingCircuitIds([]);
    if (rowIds.length === 0) {
      setError("Missing dragged device row.");
      return;
    }

    const sourceByRowId = new Map<string, string>();
    for (const rowId of rowIds) {
      const sourceCircuitId = findDeviceRowCircuitId(rowId);
      if (!sourceCircuitId) {
        setError("Invalid dragged device row.");
        return;
      }
      sourceByRowId.set(rowId, sourceCircuitId);
    }
    const sourceCircuitId = sourceByRowId.get(rowIds[0]);
    if (!sourceCircuitId) {
      setError("Invalid dragged device row source.");
      return;
    }

    // Device-row drag supports bulk selection, but target intent is always explicit:
    // move into existing circuit or create new circuit in a target section.
    if (intent.kind === "move-to-circuit") {
      if (rowIds.length === 1 && intent.circuitId === sourceCircuitId) {
        return;
      }
      const groupedBySource = new Map<string, string[]>();
      for (const rowId of rowIds) {
        const source = sourceByRowId.get(rowId)!;
        if (!groupedBySource.has(source)) {
          groupedBySource.set(source, []);
        }
        groupedBySource.get(source)!.push(rowId);
      }
      await runCommand({
        label: rowIds.length > 1 ? `Move ${rowIds.length} device rows` : "Move device row",
        redo: async () => {
          await moveCircuitDeviceRowsBulk({ rowIds, targetCircuitId: intent.circuitId });
          pendingSelectedDeviceRowIdsAfterReload.current = rowIds;
          return null;
        },
        undo: async () => {
          for (const [source, ids] of groupedBySource) {
            await moveCircuitDeviceRowsBulk({ rowIds: ids, targetCircuitId: source });
          }
          pendingSelectedDeviceRowIdsAfterReload.current = rowIds;
          return null;
        },
      });
      return;
    }

    let createdCircuitId: string | null = null;
    const groupedBySource = new Map<string, string[]>();
    for (const rowId of rowIds) {
      const source = sourceByRowId.get(rowId)!;
      if (!groupedBySource.has(source)) {
        groupedBySource.set(source, []);
      }
      groupedBySource.get(source)!.push(rowId);
    }
    await runCommand({
      label: rowIds.length > 1 ? `Move ${rowIds.length} device rows to new circuit` : "Move device row to new circuit",
      redo: async () => {
        const moved = await moveCircuitDeviceRowsBulk({
          rowIds,
          targetSectionId: intent.sectionId,
          createNewCircuit: true,
        });
        createdCircuitId = moved.createdCircuitId ?? null;
        pendingSelectedDeviceRowIdsAfterReload.current = rowIds;
        return null;
      },
      undo: async () => {
        for (const [source, ids] of groupedBySource) {
          await moveCircuitDeviceRowsBulk({ rowIds: ids, targetCircuitId: source });
        }
        if (createdCircuitId) {
          await deleteCircuitById(createdCircuitId);
        }
        pendingSelectedDeviceRowIdsAfterReload.current = rowIds;
        return null;
      },
    });
  }

  // Handles circuit drag intent and reorders whole circuit blocks within one section only.
  async function handleCircuitReorderDrop(event: DragEvent<HTMLElement>, intent: CircuitReorderDropIntent) {
    event.preventDefault();
    event.stopPropagation();
    const draggedCircuitIds = parseDraggedCircuitIds(event);
    const singleCircuitId = event.dataTransfer.getData("application/x-circuit-id") || draggingCircuitId;
    const sourceCircuitIds = draggedCircuitIds.length > 0 ? draggedCircuitIds : singleCircuitId ? [singleCircuitId] : [];
    setCircuitReorderIntent(null);
    setDraggingCircuitId(null);
    setDraggingCircuitIds([]);
    if (sourceCircuitIds.length === 0) {
      setError("Missing dragged circuit.");
      return;
    }
    if (!intent.valid) {
      setError("Cross-section circuit move is not allowed in this phase.");
      return;
    }
    const section = data?.sections.find((entry) => entry.id === intent.sectionId);
    if (!section) {
      setError("Invalid section id.");
      return;
    }
    const sectionCircuitIds = new Set(section.circuits.map((circuit) => circuit.id));
    if (sourceCircuitIds.some((id) => !sectionCircuitIds.has(id))) {
      setError("Cross-section circuit move is not allowed in this phase.");
      return;
    }
    const beforeOrder = section.circuits.map((circuit) => circuit.id);
    const primaryCircuitId = sourceCircuitIds[0];
    await runCommand({
      label: sourceCircuitIds.length > 1 ? `Reorder ${sourceCircuitIds.length} circuits` : "Reorder circuits",
      redo: async () => {
        await applyCircuitReorder(intent, sourceCircuitIds);
        pendingSelectedCircuitIdsAfterReload.current = sourceCircuitIds;
        return {
          rowKey: `circuitSummary:${primaryCircuitId}`,
          cellKey: "equipmentIdentifier",
          rowType: "circuitSummary",
          sectionId: intent.sectionId,
          circuitId: primaryCircuitId,
        };
      },
      undo: async () => {
        await reorderSectionCircuits(intent.sectionId, beforeOrder);
        pendingSelectedCircuitIdsAfterReload.current = sourceCircuitIds;
        return {
          rowKey: `circuitSummary:${primaryCircuitId}`,
          cellKey: "equipmentIdentifier",
          rowType: "circuitSummary",
          sectionId: intent.sectionId,
          circuitId: primaryCircuitId,
        };
      },
    });
  }

  // Delete undo recreates the row from captured snapshot because delete is destructive.
  async function handleDeleteDevice(rowId: string) {
    if (!confirm("Delete this device row?")) {
      return;
    }
    const sourceCircuitId = findDeviceRowCircuitId(rowId);
    const sourceCircuit = sourceCircuitId ? getCircuitSnapshot(sourceCircuitId) : null;
    const rowSnapshot = sourceCircuit?.deviceRows.find((row) => row.id === rowId);
    if (!sourceCircuitId || !rowSnapshot) {
      setError("Invalid device row id.");
      return;
    }
    let recreatedRowId: string | null = null;
    await runCommand({
      label: "Delete device row",
      redo: async () => {
        await deleteCircuitDeviceRowById(recreatedRowId ?? rowId);
        return null;
      },
      undo: async () => {
        // Recreate instead of "undelete": backend ids are immutable once deleted.
        const created = (await createCircuitDeviceRow(sourceCircuitId, {
          linkedProjectDeviceId: rowSnapshot.linkedProjectDeviceId,
          name: rowSnapshot.name,
          displayName: rowSnapshot.displayName,
          phaseType: rowSnapshot.phaseType,
          connectionKind: rowSnapshot.connectionKind,
          costGroup: rowSnapshot.costGroup,
          category: rowSnapshot.category,
          level: rowSnapshot.level,
          roomId: rowSnapshot.roomId,
          roomNumberSnapshot: rowSnapshot.roomNumberSnapshot,
          roomNameSnapshot: rowSnapshot.roomNameSnapshot,
          quantity: rowSnapshot.quantity,
          powerPerUnit: rowSnapshot.powerPerUnit,
          simultaneityFactor: rowSnapshot.simultaneityFactor,
          cosPhi: rowSnapshot.cosPhi,
          remark: rowSnapshot.remark,
          overriddenFields: rowSnapshot.overriddenFields,
          sortOrder: rowSnapshot.sortOrder,
        })) as { id: string };
        recreatedRowId = created.id;
        return null;
      },
    });
  }

  // Deletes a circuit and all child rows; undo recreates from captured snapshot.
  async function handleDeleteCircuit(circuitId: string) {
    if (!confirm("Delete this circuit and all assigned device rows?")) {
      return;
    }
    const snapshot = getCircuitSnapshot(circuitId);
    if (!snapshot) {
      setError("Invalid circuit id.");
      return;
    }
    let recreatedCircuitId: string | null = null;
    await runCommand({
      label: "Delete circuit",
      redo: async () => {
        await deleteCircuitById(recreatedCircuitId ?? circuitId);
        return null;
      },
      undo: async () => {
        const recreated = await recreateCircuit(snapshot);
        recreatedCircuitId = recreated.circuitId;
        return null;
      },
    });
  }

  // Explicit renumber action. Undo restores previous BMKs via safe section identifier update.
  async function handleRenumberSection(sectionId: string) {
    if (!confirm("Renumber this section? Only circuits in this section will change.")) {
      return;
    }
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      return;
    }
    const before = section.circuits.map((circuit) => ({
      id: circuit.id,
      equipmentIdentifier: circuit.equipmentIdentifier,
    }));
    await runCommand({
      label: "Renumber section",
      redo: async () => {
        await renumberCircuitSection(sectionId);
        return null;
      },
      undo: async () => {
        // Restore prior BMKs through safe bulk endpoint to avoid transient unique collisions.
        await updateSectionEquipmentIdentifiers(
          sectionId,
          before.map((entry) => ({
            circuitId: entry.id,
            equipmentIdentifier: entry.equipmentIdentifier,
          }))
        );
        return null;
      },
    });
  }

  // Keeps container-level keyboard focus model valid when focus returns from toolbar/popovers.
  function handleContainerFocus() {
    if (editingCell) {
      const row = findRow(editingCell.rowKey);
      const cell = row?.cells.find((entry) => entry.cellKey === editingCell.cellKey);
      if (!row || !cell || !cell.editable) {
        setEditingCell(null);
      }
      return;
    }
    if (!selectedCell && editableCells.length) {
      setSelectedCell(editableCells[0]);
    }
  }

  // Spreadsheet-like keyboard dispatcher:
  // - Enter/F2 starts edit with current value selected
  // - printable key starts overwrite edit (type-to-edit)
  // - Arrow/Tab move selectedCell when not editing
  function handleContainerKeyDown(event: KeyboardEvent<HTMLDivElement>) {
    if (editingCell) {
      if (event.key === "Tab") {
        event.preventDefault();
      }
      return;
    }
    if (event.key === "Escape") {
      if (selectedRowKeys.length > 0) {
        event.preventDefault();
        setSelectedRowKeys([]);
        setAnchorRowKey(null);
      }
      return;
    }
    if (event.ctrlKey && event.key.toLowerCase() === "z") {
      event.preventDefault();
      if (event.shiftKey) {
        void handleRedo();
      } else {
        void handleUndo();
      }
      return;
    }
    if (event.ctrlKey && event.key.toLowerCase() === "y") {
      event.preventDefault();
      void handleRedo();
      return;
    }
    const isCtrlPlus =
      event.ctrlKey &&
      (event.key === "+" || (event.shiftKey && event.key === "=") || event.code === "NumpadAdd");
    if (isCtrlPlus) {
      event.preventDefault();
      const sectionId = activeSectionId ?? data?.sections[0]?.id;
      if (sectionId) {
        void handleAddReserveCircuit(sectionId);
      }
      return;
    }
    if (event.key === "Tab" && selectedCell) {
      event.preventDefault();
      const idx = editableCells.findIndex(
        (entry) => entry.rowKey === selectedCell.rowKey && entry.cellKey === selectedCell.cellKey
      );
      if (idx >= 0) {
        const nextIdx =
          event.shiftKey ? Math.max(0, idx - 1) : Math.min(editableCells.length - 1, idx + 1);
        setSelectedCell(editableCells[nextIdx]);
      }
      return;
    }
    if (event.key === "ArrowRight") {
      event.preventDefault();
      moveHorizontal(1);
      return;
    }
    if (event.key === "ArrowLeft") {
      event.preventDefault();
      moveHorizontal(-1);
      return;
    }
    if (event.key === "ArrowDown") {
      event.preventDefault();
      moveVertical(1);
      return;
    }
    if (event.key === "ArrowUp") {
      event.preventDefault();
      moveVertical(-1);
      return;
    }
    if ((event.key === "Enter" || event.key === "F2") && selectedCell) {
      event.preventDefault();
      startEdit(selectedCell, "selectExisting");
      return;
    }
    if (isPrintableKey(event) && selectedCell) {
      // First printable key should become draft content directly; do not pre-select old text.
      event.preventDefault();
      startEdit(selectedCell, "replaceWithTypedChar", event.key);
    }
  }

  if (isLoading && !data) {
    return <div className="notice info">Loading circuit tree editor...</div>;
  }
  if (error) {
    return <div className="notice error">{error}</div>;
  }
  if (!data || data.sections.length === 0) {
    return <div className="notice muted">No sections/circuits available.</div>;
  }
  if (filteredSortedSections.length === 0) {
    return (
      <div className="tree-editor-shell">
        <div className="editor-toolbar">
          <button type="button" onClick={() => void handleUndo()} disabled={undoStack.length === 0 || historyBusy || isSaving}>
            Undo
          </button>
          <button type="button" onClick={() => void handleRedo()} disabled={redoStack.length === 0 || historyBusy || isSaving}>
            Redo
          </button>
        <button
          type="button"
          onClick={() => {
            setSortState(null);
            setColumnFilters({});
            setOpenFilterColumn(null);
          }}
          disabled={!Boolean(sortState) && !hasActiveFilters}
        >
          Clear sort/filter
        </button>
        <button type="button" onClick={() => setIsColumnMenuOpen((current) => !current)}>
          Columns
        </button>
        {isColumnMenuOpen ? (
          <div className="column-settings-menu">
            <div className="column-settings-actions">
              <button type="button" onClick={() => resetColumns()}>
                Reset columns
              </button>
            </div>
            <div className="column-settings-list">
              {orderedColumns.map((column) => {
                const visible = visibleColumnKeys.includes(column.key);
                return (
                  <div
                    key={`col-empty-${column.key}`}
                    className={`column-settings-item ${draggingColumnKey === column.key ? "dragging" : ""} ${columnDropTargetKey === column.key ? "drop-target" : ""} ${column.locked ? "locked" : ""}`}
                    draggable={!column.locked}
                    onDragStart={(event) => handleColumnDragStart(event, column.key)}
                    onDragOver={(event) => handleColumnDragOver(event, column.key)}
                    onDrop={(event) => handleColumnDrop(event, column.key)}
                    onDragEnd={() => handleColumnDragEnd()}
                  >
                    <label>
                      <input
                        type="checkbox"
                        checked={visible}
                        disabled={column.locked}
                        onChange={() => toggleColumnVisibility(column.key)}
                      />
                      <span>{column.label}</span>
                    </label>
                    <div className="column-settings-order">
                      <button type="button" onClick={() => moveColumn(column.key, -1)}>
                        ↑
                      </button>
                      <button type="button" onClick={() => moveColumn(column.key, 1)}>
                        ↓
                      </button>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ) : null}
      </div>
        <div className="notice muted">No matching circuits for active filters.</div>
      </div>
    );
  }

  const isSortedView = Boolean(sortState);
  // Renumbering is intentionally blocked while sort/filter overlays are active so users
  // cannot renumber against a transformed view that has not been persisted yet.
  const hasActiveSortOrFilter = isSortedView || hasActiveFilters;
  const draggingDeviceCount = draggingDeviceRowIds.length > 0 ? draggingDeviceRowIds.length : draggingDeviceRowId ? 1 : 0;
  const activeDraggedCircuitIds =
    draggingCircuitIds.length > 0 ? draggingCircuitIds : draggingCircuitId ? [draggingCircuitId] : [];
  const draggingCircuitCount = activeDraggedCircuitIds.length;

  return (
    <div className="tree-editor-shell">
      <div className="editor-toolbar">
        <button type="button" onClick={() => void handleUndo()} disabled={undoStack.length === 0 || historyBusy || isSaving}>
          Undo
        </button>
        <button type="button" onClick={() => void handleRedo()} disabled={redoStack.length === 0 || historyBusy || isSaving}>
          Redo
        </button>
        {isSortedView ? (
          <button
            type="button"
            onClick={() => void handleApplySortedOrder()}
            disabled={hasActiveFilters || isSaving || historyBusy}
            title={hasActiveFilters ? "Clear filters first to apply sorted order." : undefined}
          >
            Apply sorted order
          </button>
        ) : null}
        <button type="button" onClick={() => setIsColumnMenuOpen((current) => !current)}>
          Columns
        </button>
        <button
          type="button"
          onClick={() => {
            setSortState(null);
            setColumnFilters({});
            setOpenFilterColumn(null);
          }}
          disabled={!isSortedView && !hasActiveFilters}
        >
          Clear sort/filter
        </button>
        {isColumnMenuOpen ? (
          <div className="column-settings-menu">
            <div className="column-settings-actions">
              <button type="button" onClick={() => resetColumns()}>
                Reset columns
              </button>
            </div>
            <div className="column-settings-list">
              {orderedColumns.map((column) => {
                const visible = visibleColumnKeys.includes(column.key);
                return (
                  <div
                    key={`col-${column.key}`}
                    className={`column-settings-item ${draggingColumnKey === column.key ? "dragging" : ""} ${columnDropTargetKey === column.key ? "drop-target" : ""} ${column.locked ? "locked" : ""}`}
                    draggable={!column.locked}
                    onDragStart={(event) => handleColumnDragStart(event, column.key)}
                    onDragOver={(event) => handleColumnDragOver(event, column.key)}
                    onDrop={(event) => handleColumnDrop(event, column.key)}
                    onDragEnd={() => handleColumnDragEnd()}
                  >
                    <label>
                      <input
                        type="checkbox"
                        checked={visible}
                        disabled={column.locked}
                        onChange={() => toggleColumnVisibility(column.key)}
                      />
                      <span>{column.label}</span>
                    </label>
                    <div className="column-settings-order">
                      <button type="button" onClick={() => moveColumn(column.key, -1)}>
                        ↑
                      </button>
                      <button type="button" onClick={() => moveColumn(column.key, 1)}>
                        ↓
                      </button>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ) : null}
      </div>
      {hasActiveSortOrFilter ? (
        <div className="notice muted">Sorting/filtering is view-only. Renumber is disabled while sort/filter is active.</div>
      ) : null}
      {isSortedView && hasActiveFilters ? (
        <div className="notice muted">Apply sorted order is disabled while filters are active.</div>
      ) : null}
      {isSaving ? <div className="notice info">Saving...</div> : null}
      <div className="tree-editor-layout">
        <aside className="project-device-sidebar">
          <h3>Project devices</h3>
          <input
            type="text"
            placeholder="Search name/displayName..."
            value={projectDeviceSearch}
            onChange={(event) => setProjectDeviceSearch(event.target.value)}
          />
          <div className="project-device-list">
            {searchableProjectDevices.map((device) => (
              <button
                key={device.id}
                type="button"
                className={`project-device-item ${selectedProjectDeviceId === device.id ? "selected" : ""} ${draggingProjectDeviceId === device.id ? "dragging" : ""}`}
                onClick={() => setSelectedProjectDeviceId(device.id)}
                draggable
                onDragStart={(event) => {
                  setSelectedProjectDeviceId(device.id);
                  setDraggingProjectDeviceId(device.id);
                  setDraggingDeviceRowId(null);
                  setDraggingDeviceRowIds([]);
                  setDraggingCircuitId(null);
                  setDraggingCircuitIds([]);
                  setDeviceMoveIntent(null);
                  setCircuitReorderIntent(null);
                  event.dataTransfer.effectAllowed = "copy";
                  event.dataTransfer.setData("application/x-project-device-id", device.id);
                }}
                onDragEnd={() => {
                  setDraggingProjectDeviceId(null);
                  setDropIntent(null);
                  setDraggingDeviceRowIds([]);
                  setDraggingCircuitIds([]);
                }}
              >
                <strong>{device.displayName || device.name}</strong>
                <span>Name: {device.name}</span>
                <span>Phase: {device.phaseType}</span>
                <span>Qty: {device.quantity}</span>
                <span>P/unit: {device.powerPerUnit}</span>
                <span>g: {device.simultaneityFactor}</span>
                <span>Total: {device.totalPower}</span>
                <span>Cost group: {device.costGroup || "-"}</span>
                <span>Category: {device.category || "-"}</span>
              </button>
            ))}
            {searchableProjectDevices.length === 0 ? <p className="notice muted">No matching project devices.</p> : null}
          </div>
          <div className="sidebar-actions">
            <label>
              Target section
              <select
                value={targetSectionId ?? ""}
                onChange={(event) => setTargetSectionId(event.target.value || null)}
              >
                <option value="">Select section...</option>
                {data.sections.map((section) => (
                  <option key={section.id} value={section.id}>
                    {section.displayName}
                  </option>
                ))}
              </select>
            </label>
            <button type="button" onClick={() => void handleAddProjectDeviceAsNewCircuit()}>
              Add as new circuit
            </button>
            <label>
              Target circuit
              <select
                value={targetCircuitId ?? ""}
                onChange={(event) => setTargetCircuitId(event.target.value || null)}
              >
                <option value="">Use selected row circuit...</option>
                {circuitOptions.map((option) => (
                  <option key={option.id} value={option.id}>
                    {option.label}
                  </option>
                ))}
              </select>
            </label>
            <button type="button" onClick={() => void handleAddProjectDeviceToCircuit()}>
              Add to selected circuit
            </button>
          </div>
        </aside>
        <div
          className="tree-grid-wrap"
          ref={containerRef}
          tabIndex={0}
          onFocus={handleContainerFocus}
          onKeyDown={handleContainerKeyDown}
          onMouseDown={(event) => {
            if (event.target === event.currentTarget) {
              setSelectedRowKeys([]);
              setAnchorRowKey(null);
            }
          }}
        >
          <table className="tree-grid">
          <thead>
            <tr>
              {visibleColumns.map((column) => (
                <th key={column.key} className={column.numeric ? "num" : ""}>
                  <div className="header-cell">
                    <button
                      type="button"
                      className="header-sort-btn"
                      onClick={() => {
                        setSortState((current) => {
                          if (!current || current.key !== column.key) {
                            return { key: column.key, direction: "asc" };
                          }
                          if (current.direction === "asc") {
                            return { key: column.key, direction: "desc" };
                          }
                          return null;
                        });
                      }}
                    >
                      {column.label}
                      {sortState?.key === column.key ? (sortState.direction === "asc" ? " ↑" : " ↓") : ""}
                    </button>
                    <button
                      type="button"
                      className={`header-filter-btn ${(columnFilters[column.key]?.length ?? 0) > 0 ? "active" : ""}`}
                      onClick={() => setOpenFilterColumn((current) => (current === column.key ? null : column.key))}
                    >
                      Filter
                    </button>
                  </div>
                  {openFilterColumn === column.key ? (
                    <div className="header-filter-menu">
                      <button
                        type="button"
                        className="header-filter-clear"
                        onClick={() =>
                          setColumnFilters((current) => ({
                            ...current,
                            [column.key]: [],
                          }))
                        }
                      >
                        Clear
                      </button>
                      <div className="header-filter-values">
                        {distinctValuesByColumn[column.key].map((value) => {
                          const selected = columnFilters[column.key] ?? [];
                          const checked = selected.includes(value);
                          return (
                            <label key={`${column.key}-${value}`} className="header-filter-item">
                              <input
                                type="checkbox"
                                checked={checked}
                                onChange={(event) => {
                                  setColumnFilters((current) => {
                                    const previous = current[column.key] ?? [];
                                    const next = event.target.checked
                                      ? [...previous, value]
                                      : previous.filter((entry) => entry !== value);
                                    return { ...current, [column.key]: next };
                                  });
                                }}
                              />
                              <span>{value}</span>
                            </label>
                          );
                        })}
                      </div>
                    </div>
                  ) : null}
                </th>
              ))}
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {visibleRows.map((row) => {
              if (row.rowType === "section") {
                const section = data.sections.find((entry) => entry.id === row.sectionId)!;
                return (
                  <tr
                    key={row.rowKey}
                    className={`section-row ${
                      dropIntent?.kind === "new-circuit" && dropIntent.sectionId === section.id ? "drop-target-active" : ""
                    }`}
                    onDragOver={(event) => {
                      if (draggingCircuitCount > 0) {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = "none";
                        setCircuitReorderIntent({
                          kind: "section-end",
                          sectionId: section.id,
                          valid: false,
                        });
                        return;
                      }
                      if (!draggingProjectDeviceId) {
                        return;
                      }
                      event.preventDefault();
                      event.dataTransfer.dropEffect = "copy";
                      setDropIntent({ kind: "new-circuit", sectionId: section.id });
                    }}
                    onDragLeave={() => {
                      if (dropIntent?.kind === "new-circuit" && dropIntent.sectionId === section.id) {
                        setDropIntent(null);
                      }
                      if (circuitReorderIntent?.kind === "section-end" && circuitReorderIntent.sectionId === section.id) {
                        setCircuitReorderIntent(null);
                      }
                    }}
                    onDrop={(event) => {
                      if (draggingProjectDeviceId) {
                        void handleDropWithIntent(event, { kind: "new-circuit", sectionId: section.id });
                        return;
                      }
                      if (draggingCircuitCount > 0) {
                        void handleCircuitReorderDrop(event, {
                          kind: "section-end",
                          sectionId: section.id,
                          valid: false,
                        });
                      }
                    }}
                  >
                    <td colSpan={visibleColumns.length + 1} className="section-drop-cell">
                      <div className="section-content">
                        <strong>{section.displayName}</strong>
                        <div className="section-actions">
                          <button type="button" tabIndex={-1} onClick={() => void handleAddReserveCircuit(section.id)}>
                            Add circuit
                          </button>
                          <button
                            type="button"
                            tabIndex={-1}
                            disabled={hasActiveSortOrFilter}
                            onClick={() => void handleRenumberSection(section.id)}
                            title={hasActiveSortOrFilter ? "Clear sort/filter first to renumber safely." : undefined}
                          >
                            Renumber section
                          </button>
                        </div>
                      </div>
                      {dropIntent?.kind === "new-circuit" && dropIntent.sectionId === section.id ? (
                        <span className="drop-hint">drop here to create new circuit</span>
                      ) : null}
                    </td>
                  </tr>
                );
              }

              return (
                <tr
                  key={row.rowKey}
                  className={`${
                    row.rowType === "circuitSummary"
                      ? "summary-row"
                      : row.rowType === "deviceRow"
                        ? "device-row"
                        : row.rowType === "reserveCircuit"
                          ? "empty-circuit-row"
                          : row.rowType === "placeholder"
                            ? "placeholder-row"
                            : ""
                  } ${
                    row.rowType === "placeholder" &&
                    ((dropIntent?.kind === "new-circuit" && dropIntent.sectionId === row.sectionId) ||
                      (deviceMoveIntent?.kind === "move-to-new-circuit" && deviceMoveIntent.sectionId === row.sectionId) ||
                      (circuitReorderIntent?.kind === "section-end" && circuitReorderIntent.sectionId === row.sectionId))
                      ? "drop-target-active"
                      : ""
                  } ${
                    row.rowType === "placeholder" &&
                    circuitReorderIntent?.kind === "section-end" &&
                    circuitReorderIntent.sectionId === row.sectionId &&
                    !circuitReorderIntent.valid
                      ? "drop-target-invalid"
                      : ""
                  } ${
                    row.circuit &&
                    circuitReorderIntent &&
                    (circuitReorderIntent.kind === "before-circuit" || circuitReorderIntent.kind === "after-circuit") &&
                    circuitReorderIntent.targetCircuitId === row.circuit.id &&
                    circuitReorderIntent.valid
                      ? "drop-target-active"
                      : ""
                  } ${
                    row.circuit &&
                    circuitReorderIntent?.kind === "before-circuit" &&
                    circuitReorderIntent.targetCircuitId === row.circuit.id &&
                    circuitReorderIntent.valid
                      ? "circuit-insert-before"
                      : ""
                  } ${
                    row.circuit &&
                    circuitReorderIntent?.kind === "after-circuit" &&
                    circuitReorderIntent.targetCircuitId === row.circuit.id &&
                    circuitReorderIntent.valid
                      ? "circuit-insert-after"
                      : ""
                  } ${
                    row.circuit &&
                    circuitReorderIntent &&
                    (circuitReorderIntent.kind === "before-circuit" || circuitReorderIntent.kind === "after-circuit") &&
                    circuitReorderIntent.targetCircuitId === row.circuit.id &&
                    !circuitReorderIntent.valid
                      ? "drop-target-invalid"
                      : ""
                  } ${
                    row.circuit?.id &&
                    ((draggingCircuitIds.length > 0 && draggingCircuitIds.includes(row.circuit.id)) ||
                      (draggingCircuitIds.length === 0 && draggingCircuitId && row.circuit.id === draggingCircuitId))
                      ? "circuit-dragging-block"
                      : ""
                  } ${selectedRowKeys.includes(row.rowKey) ? "row-selected" : ""}
                  }`}
                  onClick={() => setActiveSectionId(row.sectionId)}
                  onDragOver={(event) => {
                    if (draggingCircuitCount > 0) {
                      const sourceSectionIds = activeDraggedCircuitIds
                        .map((id) => findCircuitSectionId(id))
                        .filter((id): id is string => Boolean(id));
                      const valid = sourceSectionIds.length > 0 && sourceSectionIds.every((sectionId) => sectionId === row.sectionId);
                      if (row.rowType === "placeholder") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = valid ? "move" : "none";
                        setCircuitReorderIntent({ kind: "section-end", sectionId: row.sectionId, valid });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        if (activeDraggedCircuitIds.includes(row.circuit.id)) {
                          setCircuitReorderIntent(null);
                          return;
                        }
                        const rect = (event.currentTarget as HTMLTableRowElement).getBoundingClientRect();
                        const isAfter = event.clientY > rect.top + rect.height / 2;
                        event.preventDefault();
                        event.dataTransfer.dropEffect = valid ? "move" : "none";
                        setCircuitReorderIntent({
                          kind: isAfter ? "after-circuit" : "before-circuit",
                          sectionId: row.sectionId,
                          targetCircuitId: row.circuit.id,
                          valid,
                        });
                      }
                      return;
                    }
                    if (draggingProjectDeviceId) {
                      if (row.rowType === "placeholder") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = "copy";
                        setDropIntent({ kind: "new-circuit", sectionId: row.sectionId });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = "copy";
                        setDropIntent({ kind: "add-to-circuit", circuitId: row.circuit.id, sectionId: row.sectionId });
                      }
                      return;
                    }
                    if (draggingDeviceCount > 0) {
                      if (row.rowType === "placeholder") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = "move";
                        setDeviceMoveIntent({ kind: "move-to-new-circuit", sectionId: row.sectionId });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        const sourceCircuitIds = (draggingDeviceRowIds.length > 0 ? draggingDeviceRowIds : draggingDeviceRowId ? [draggingDeviceRowId] : [])
                          .map((id) => findDeviceRowCircuitId(id))
                          .filter((id): id is string => Boolean(id));
                        if (sourceCircuitIds.some((sourceCircuitId) => sourceCircuitId !== row.circuit!.id)) {
                          event.preventDefault();
                          event.dataTransfer.dropEffect = "move";
                          setDeviceMoveIntent({ kind: "move-to-circuit", circuitId: row.circuit.id, sectionId: row.sectionId });
                        }
                      }
                    }
                  }}
                  onDragLeave={() => {
                    setDropIntent((current) => {
                      if (!current) {
                        return null;
                      }
                      if (current.kind === "new-circuit" && row.rowType === "placeholder" && current.sectionId === row.sectionId) {
                        return null;
                      }
                      if (current.kind === "add-to-circuit" && current.circuitId === row.circuit?.id) {
                        return null;
                      }
                      return current;
                    });
                    setDeviceMoveIntent((current) => {
                      if (!current) {
                        return null;
                      }
                      if (current.kind === "move-to-new-circuit" && row.rowType === "placeholder" && current.sectionId === row.sectionId) {
                        return null;
                      }
                      if (current.kind === "move-to-circuit" && current.circuitId === row.circuit?.id) {
                        return null;
                      }
                      return current;
                    });
                    setCircuitReorderIntent((current) => {
                      if (!current) {
                        return null;
                      }
                      if (current.kind === "section-end" && row.rowType === "placeholder" && current.sectionId === row.sectionId) {
                        return null;
                      }
                      if (
                        (current.kind === "before-circuit" || current.kind === "after-circuit") &&
                        current.targetCircuitId === row.circuit?.id
                      ) {
                        return null;
                      }
                      return current;
                    });
                  }}
                  onDrop={(event) => {
                    if (draggingCircuitCount > 0) {
                      if (row.rowType === "placeholder") {
                        const sourceSectionIds = activeDraggedCircuitIds
                          .map((id) => findCircuitSectionId(id))
                          .filter((id): id is string => Boolean(id));
                        void handleCircuitReorderDrop(event, {
                          kind: "section-end",
                          sectionId: row.sectionId,
                          valid: sourceSectionIds.length > 0 && sourceSectionIds.every((sectionId) => sectionId === row.sectionId),
                        });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        if (activeDraggedCircuitIds.includes(row.circuit.id)) {
                          return;
                        }
                        const sourceSectionIds = activeDraggedCircuitIds
                          .map((id) => findCircuitSectionId(id))
                          .filter((id): id is string => Boolean(id));
                        const rect = (event.currentTarget as HTMLTableRowElement).getBoundingClientRect();
                        const isAfter = event.clientY > rect.top + rect.height / 2;
                        void handleCircuitReorderDrop(event, {
                          kind: isAfter ? "after-circuit" : "before-circuit",
                          sectionId: row.sectionId,
                          targetCircuitId: row.circuit.id,
                          valid: sourceSectionIds.length > 0 && sourceSectionIds.every((sectionId) => sectionId === row.sectionId),
                        });
                      }
                      return;
                    }
                    if (draggingProjectDeviceId) {
                      if (row.rowType === "placeholder") {
                        void handleDropWithIntent(event, { kind: "new-circuit", sectionId: row.sectionId });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        void handleDropWithIntent(event, {
                          kind: "add-to-circuit",
                          circuitId: row.circuit.id,
                          sectionId: row.sectionId,
                        });
                      }
                      return;
                    }
                    if (draggingDeviceCount > 0) {
                      if (row.rowType === "placeholder") {
                        void handleDeviceRowDropWithIntent(event, { kind: "move-to-new-circuit", sectionId: row.sectionId });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        void handleDeviceRowDropWithIntent(event, {
                          kind: "move-to-circuit",
                          circuitId: row.circuit.id,
                          sectionId: row.sectionId,
                        });
                      }
                    }
                  }}
                >
                  {visibleColumns.map((column) => {
                    const cell = row.cells.find((entry) => entry.cellKey === column.key)!;
                    const isSelected =
                      selectedCell?.rowKey === row.rowKey && selectedCell.cellKey === column.key;
                    const isEditing =
                      editingCell?.rowKey === row.rowKey && editingCell.cellKey === column.key;
                    return (
                      <td
                        key={column.key}
                        className={`${column.numeric ? "num" : ""} ${cell.editable ? "cell-editable" : ""} ${isSelected ? "cell-selected" : ""} ${
                          Boolean(row.device) && column.key === "displayName" && (row.rowType === "deviceRow" || row.rowType === "circuitCompact")
                            ? "device-drag-handle"
                            : ""
                        } ${
                          column.key === "equipmentIdentifier" &&
                          Boolean(row.circuit) &&
                          (row.rowType === "circuitCompact" || row.rowType === "circuitSummary" || row.rowType === "reserveCircuit")
                            ? "circuit-drag-handle"
                            : ""
                        } ${
                          row.device?.id &&
                          ((draggingDeviceRowIds.length > 0 && draggingDeviceRowIds.includes(row.device.id)) ||
                            (draggingDeviceRowIds.length === 0 && draggingDeviceRowId === row.device.id))
                            ? "device-dragging"
                            : ""
                        }`}
                        draggable={
                          !isEditing &&
                          ((Boolean(row.device) &&
                            column.key === "displayName" &&
                            (row.rowType === "deviceRow" || row.rowType === "circuitCompact")) ||
                            (Boolean(row.circuit) &&
                              column.key === "equipmentIdentifier" &&
                              (row.rowType === "circuitCompact" ||
                                row.rowType === "circuitSummary" ||
                                row.rowType === "reserveCircuit")))
                        }
                        onDragStart={(event) => {
                          if (
                            row.circuit &&
                            column.key === "equipmentIdentifier" &&
                            (row.rowType === "circuitCompact" ||
                              row.rowType === "circuitSummary" ||
                              row.rowType === "reserveCircuit")
                          ) {
                            const circuitIds = getSelectedEligibleCircuitIds(row.circuit.id);
                            setDraggingProjectDeviceId(null);
                            setDropIntent(null);
                            setDraggingDeviceRowId(null);
                            setDraggingDeviceRowIds([]);
                            setDeviceMoveIntent(null);
                            setDraggingCircuitId(row.circuit.id);
                            setDraggingCircuitIds(circuitIds);
                            setCircuitReorderIntent(null);
                            event.dataTransfer.effectAllowed = "move";
                            event.dataTransfer.setData("application/x-circuit-id", row.circuit.id);
                            event.dataTransfer.setData("application/x-circuit-ids", JSON.stringify(circuitIds));
                            return;
                          }
                          if (
                            row.device &&
                            column.key === "displayName" &&
                            (row.rowType === "deviceRow" || row.rowType === "circuitCompact")
                          ) {
                            const rowIds = getSelectedEligibleDeviceRowIds(row.device.id);
                            setDraggingProjectDeviceId(null);
                            setDropIntent(null);
                            setDraggingCircuitId(null);
                            setDraggingCircuitIds([]);
                            setCircuitReorderIntent(null);
                            setDraggingDeviceRowId(row.device.id);
                            setDraggingDeviceRowIds(rowIds);
                            setDeviceMoveIntent(null);
                            event.dataTransfer.effectAllowed = "move";
                            event.dataTransfer.setData("application/x-circuit-device-row-id", row.device.id);
                            event.dataTransfer.setData("application/x-circuit-device-row-ids", JSON.stringify(rowIds));
                          }
                        }}
                        onDragEnd={() => {
                          setDraggingDeviceRowId(null);
                          setDraggingDeviceRowIds([]);
                          setDeviceMoveIntent(null);
                          setDraggingCircuitId(null);
                          setDraggingCircuitIds([]);
                          setCircuitReorderIntent(null);
                        }}
                        onClick={(event) => {
                          if (cell.editable) {
                            handleRowSelectionClick(row, column.key, {
                              ctrlKey: event.ctrlKey,
                              metaKey: event.metaKey,
                              shiftKey: event.shiftKey,
                            });
                          }
                        }}
                        onDoubleClick={() => {
                          if (cell.editable) {
                            startEdit({ rowKey: row.rowKey, cellKey: column.key }, "selectExisting");
                          }
                        }}
                      >
                        {isEditing ? (
                          <input
                            ref={inputRef}
                            value={editingCell.draft}
                            onChange={(event) =>
                              setEditingCell((current) =>
                                current ? { ...current, draft: event.target.value } : current
                              )
                            }
                            onKeyDown={(event) => {
                              if (event.key === "Enter") {
                                event.preventDefault();
                                void commitEdit("stay");
                              } else if (event.key === "Escape") {
                                event.preventDefault();
                                cancelEdit();
                              } else if (event.key === "Tab") {
                                event.preventDefault();
                                void commitEdit(event.shiftKey ? "prev" : "next");
                              }
                            }}
                            onBlur={() => {
                              requestAnimationFrame(() => {
                                const active = document.activeElement as HTMLElement | null;
                                if (!active || !containerRef.current?.contains(active)) {
                                  setEditingCell(null);
                                  requestAnimationFrame(() => containerRef.current?.focus());
                                }
                              });
                            }}
                          />
                        ) : (
                          formatValue(cell.value)
                        )}
                      </td>
                    );
                  })}
                  <td
                    className={`action-cell ${
                      (dropIntent?.kind === "add-to-circuit" && dropIntent.circuitId === row.circuit?.id) ||
                      (deviceMoveIntent?.kind === "move-to-circuit" && deviceMoveIntent.circuitId === row.circuit?.id)
                        ? "drop-target-active"
                        : ""
                    }`}
                  >
                    {row.circuit && row.rowType !== "deviceRow" ? (
                      <>
                        <button
                          type="button"
                          tabIndex={-1}
                          onClick={() => void handleAddManualDevice(row.circuit!, row.sectionId)}
                        >
                          Add manual device
                        </button>
                        <button
                          type="button"
                          tabIndex={-1}
                          onClick={() => void handleDeleteCircuit(row.circuit!.id)}
                        >
                          Delete circuit
                        </button>
                      </>
                    ) : null}
                    {row.device ? (
                      <button type="button" tabIndex={-1} onClick={() => void handleDeleteDevice(row.device!.id)}>
                        Delete device
                      </button>
                    ) : null}
                    {dropIntent?.kind === "new-circuit" && row.rowType === "placeholder" && dropIntent.sectionId === row.sectionId ? (
                      <span className="drop-hint">new circuit in this section</span>
                    ) : null}
                    {dropIntent?.kind === "add-to-circuit" && dropIntent.circuitId === row.circuit?.id ? (
                      <span className="drop-hint">add to this circuit</span>
                    ) : null}
                    {deviceMoveIntent?.kind === "move-to-new-circuit" &&
                    row.rowType === "placeholder" &&
                    deviceMoveIntent.sectionId === row.sectionId ? (
                      <span className="drop-hint">{`move ${draggingDeviceCount || 1} device${draggingDeviceCount === 1 ? "" : "s"} to new circuit`}</span>
                    ) : null}
                    {deviceMoveIntent?.kind === "move-to-circuit" && deviceMoveIntent.circuitId === row.circuit?.id ? (
                      <span className="drop-hint">{`move ${draggingDeviceCount || 1} device${draggingDeviceCount === 1 ? "" : "s"} to this circuit`}</span>
                    ) : null}
                    {circuitReorderIntent?.kind === "section-end" &&
                    row.rowType === "placeholder" &&
                    circuitReorderIntent.sectionId === row.sectionId ? (
                      <span className="drop-hint">
                        {circuitReorderIntent.valid
                          ? `move ${draggingCircuitCount || 1} circuit${draggingCircuitCount === 1 ? "" : "s"} to section end`
                          : "cross-section move not allowed"}
                      </span>
                    ) : null}
                    {circuitReorderIntent &&
                    (circuitReorderIntent.kind === "before-circuit" || circuitReorderIntent.kind === "after-circuit") &&
                    circuitReorderIntent.targetCircuitId === row.circuit?.id ? (
                      <span className="drop-hint">
                        {circuitReorderIntent.valid
                          ? circuitReorderIntent.kind === "before-circuit"
                            ? `move ${draggingCircuitCount || 1} circuit${draggingCircuitCount === 1 ? "" : "s"} before this circuit`
                            : `move ${draggingCircuitCount || 1} circuit${draggingCircuitCount === 1 ? "" : "s"} after this circuit`
                          : "cross-section move not allowed"}
                      </span>
                    ) : null}
                  </td>
                </tr>
              );
            })}
          </tbody>
          </table>
        </div>
      </div>
      <p className="todo-hint">TODO Phase: Ctrl+Plus currently adds circuit at end of active section.</p>
    </div>
  );
}
