"use client";

import { DragEvent, KeyboardEvent, useEffect, useMemo, useRef, useState } from "react";
import {
  inferProjectDeviceSectionKey,
  isProjectDevicePlacementValid,
} from "../../domain/services/project-device-placement.service";
import {
  resolveCircuitPhaseType,
  resolveProjectVoltage,
} from "../../domain/services/project-voltage.service";
import {
  getInsertionSortOrder,
  isGridInsertionRowType,
  resolveGridInsertionIntent,
} from "../utils/circuit-grid-insertion";
import {
  findDuplicateEquipmentIdentifierCircuitIds,
  hasEquipmentIdentifierConflict,
  requiresCrossSectionMoveConfirmation,
  resolveGridDeleteIntent,
} from "../utils/circuit-grid-safety";
import {
  allColumns,
  buildCircuitEditPatch,
  buildDeviceRowEditPatch,
  defaultVisibleColumnKeys,
  deviceFieldKeys,
  formatPhaseTypeLabel,
  formatValue,
  getCircuitSectionLabel,
  getProjectColumnLayoutStorageKey,
  isGridEditorControlTarget,
  normalizeColumnOrder,
  parseStoredColumnLayout,
} from "../utils/circuit-grid-model";
import {
  buildCircuitDeviceRowInsertSnapshot,
  buildCircuitInsertSnapshot,
} from "../utils/circuit-structure-command";
import {
  buildCircuitDeviceRowMoveAssignments,
} from "../utils/circuit-device-row-move-command";
import {
  buildCircuitSectionReorderAssignments,
} from "../utils/circuit-section-reorder-command";
import {
  buildCircuitSectionRenumberAssignments,
} from "../utils/circuit-section-renumber-command";
import {
  buildNewCircuitGroupSnapshot,
  canDeleteCircuitGroup,
  renameCircuitGroupSnapshot,
  toCircuitGroupSnapshot,
} from "../utils/circuit-group-editing";
import {
  buildDistributionBoardComponentSnapshot,
  getNextComponentSortOrder,
  getSuggestedGroupComponentIdentifier,
  toDistributionBoardComponentSnapshot,
  updateDistributionBoardComponentSnapshot,
  type DistributionBoardComponentEditorValues,
  type MutableDistributionBoardComponentRole,
} from "../utils/distribution-board-component-editing";
import { loadCircuitEditorSnapshot } from "../utils/circuit-editor-history";
import type {
  CellKey,
  ColumnDef,
  RowType,
} from "../utils/circuit-grid-model";
import {
  buildVisibleGridRowsWithStructure,
  filterAndSortCircuitSections,
  getDistinctFilterValues,
} from "../utils/circuit-grid-projection";
import type { VisibleGridRow } from "../utils/circuit-grid-projection";
import {
  deleteCircuitCommand,
  deleteCircuitDeviceRowCommand,
  deleteCircuitGroupCommand,
  deleteDistributionBoardComponentCommand,
  getCircuitTree,
  getNextCircuitIdentifier,
  getProjectHistory,
  insertCircuitCommand,
  insertCircuitDeviceRowCommand,
  insertCircuitGroupCommand,
  insertDistributionBoardComponentCommand,
  listProjectDevices,
  moveCircuitDeviceRowsCommand,
  moveCircuitDeviceRowsToNewCircuitCommand,
  reorderCircuitSectionCommand,
  reorderCircuitSectionsCommand,
  renumberCircuitSectionCommand,
  redoProjectCommand,
  updateCircuitById,
  updateCircuitDeviceRowById,
  updateCircuitGroupCommand,
  updateDistributionBoardComponentCommand,
  undoProjectCommand,
} from "../utils/api";
import type {
  CircuitTreeCircuitDto,
  CircuitTreeComponentDto,
  CircuitTreeResponseDto,
  CreateCircuitDeviceRowInputDto,
  CreateCircuitInputDto,
  ProjectCommandResultDto,
  ProjectDeviceDto,
  ProjectHistoryStateDto,
} from "../types";
import type {
  CircuitDeviceRowSnapshot,
} from "../../domain/models/circuit-device-row-structure-project-command.model";
import type {
  CircuitSnapshot,
} from "../../domain/models/circuit-structure-project-command.model";
import { DistributionBoardComponentModal } from "./distribution-board-component-modal";
import { CircuitGroupModal } from "./circuit-group-modal";
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group";

type SaveDirection = "stay" | "next" | "prev";
type StartEditMode = "selectExisting" | "replaceWithTypedChar";
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 HistoryCommand {
  label: string;
  redo: () => Promise<SelectionIntent | null | void>;
}

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

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

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 LEGACY_COLUMN_LAYOUT_STORAGE_KEY =
  "circuitTreeEditor.columnLayout.v1";

function normalizeUiError(err: unknown): string {
  const message = err instanceof Error ? err.message : "Der Vorgang ist fehlgeschlagen.";
  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") || message.includes("Ungültiger Zahlenwert")) {
    return "Bitte einen gültigen Zahlenwert eingeben.";
  }
  return message;
}

type StructureComponentEditorIntent =
  | {
      kind: "create";
      role: MutableDistributionBoardComponentRole;
      sectionId?: string;
      initialEquipmentIdentifier?: string;
    }
  | {
      kind: "edit";
      component: CircuitTreeComponentDto;
    };

type CircuitGroupEditorIntent =
  | { kind: "create" }
  | { kind: "edit"; section: CircuitTreeResponseDto["sections"][number] };

function getFullColumnLabel(column: ColumnDef): string {
  return column.fullLabel ?? column.label;
}

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

function createValuesFromEditPatch(
  patch: Record<string, unknown>
): Record<string, unknown> {
  return Object.fromEntries(
    Object.entries(patch).filter(
      ([, value]) => value !== null && value !== ""
    )
  );
}

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 [componentEditorIntent, setComponentEditorIntent] =
    useState<StructureComponentEditorIntent | null>(null);
  const [circuitGroupEditorIntent, setCircuitGroupEditorIntent] =
    useState<CircuitGroupEditorIntent | null>(null);
  const [projectDevices, setProjectDevices] = useState<ProjectDeviceDto[]>([]);
  const [isProjectDeviceDrawerOpen, setIsProjectDeviceDrawerOpen] =
    useState(false);
  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);
  const [historyState, setHistoryState] = useState<ProjectHistoryStateDto | null>(null);
  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 [filterDraftValues, setFilterDraftValues] = useState<string[]>([]);
  const [filterValueSearch, setFilterValueSearch] = useState("");
  const [visibleColumnKeys, setVisibleColumnKeys] = useState<CellKey[]>(defaultVisibleColumnKeys);
  const [columnOrder, setColumnOrder] = useState<CellKey[]>(allColumns.map((column) => column.key));
  const [
    loadedColumnLayoutProjectId,
    setLoadedColumnLayoutProjectId,
  ] = useState<string | null>(null);
  const [isColumnMenuOpen, setIsColumnMenuOpen] = useState(false);
  const [columnSearch, setColumnSearch] = useState("");
  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 | HTMLSelectElement | null>(null);
  const focusTokenRef = useRef(1);
  const projectRevisionRef = useRef<number | null>(null);

  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, history } = await loadCircuitEditorSnapshot(
        () => getCircuitTree(projectId, circuitListId),
        () => getProjectHistory(projectId)
      );
      projectRevisionRef.current = history.currentRevision;
      setHistoryState(history);
      setData(tree);
    } catch (err) {
      setError(normalizeUiError(err));
    } finally {
      if (showLoading) {
        setIsLoading(false);
      }
    }
  }

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

  async function loadProjectDeviceList() {
    const list = await listProjectDevices(projectId);
    setProjectDevices(list);
  }

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

  useEffect(() => {
    setLoadedColumnLayoutProjectId(null);
    setColumnOrder(allColumns.map((column) => column.key));
    setVisibleColumnKeys(defaultVisibleColumnKeys);
    try {
      const parsed = parseStoredColumnLayout(
        localStorage.getItem(
          getProjectColumnLayoutStorageKey(projectId)
        ) ??
          localStorage.getItem(
            LEGACY_COLUMN_LAYOUT_STORAGE_KEY
          )
      );
      if (!parsed) {
        return;
      }
      setColumnOrder(parsed.order);
      setVisibleColumnKeys(parsed.visible);
    } catch {
      // ignore invalid local storage and use defaults
    } finally {
      setLoadedColumnLayoutProjectId(projectId);
    }
  }, [projectId]);

  useEffect(() => {
    if (loadedColumnLayoutProjectId !== projectId) {
      return;
    }
    const payload = {
      order: columnOrder,
      visible: visibleColumnKeys,
    };
    localStorage.setItem(
      getProjectColumnLayoutStorageKey(projectId),
      JSON.stringify(payload)
    );
  }, [
    columnOrder,
    loadedColumnLayoutProjectId,
    projectId,
    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 activeColumnFilters = useMemo(
    () =>
      visibleColumns.flatMap((column) => {
        const values = columnFilters[column.key] ?? [];
        return values.length > 0 ? [{ column, values }] : [];
      }),
    [columnFilters, visibleColumns]
  );

  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.
    return getDistinctFilterValues(data?.sections ?? [], visibleColumns);
  }, [data, visibleColumns]);

  const filteredSortedSections = useMemo(() => {
    // Filtering and sorting are frontend view state. Backend sort order remains unchanged
    // until user explicitly applies sorted order.
    return filterAndSortCircuitSections(data?.sections ?? [], columnFilters, sortState);
  }, [data, columnFilters, 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(() => {
    return buildVisibleGridRowsWithStructure(filteredSortedSections, {
      headerComponents: data?.headerComponents ?? [],
      footerComponents: data?.footerComponents ?? [],
    });
  }, [data, filteredSortedSections]);

  function clearColumnFilter(key: CellKey) {
    setColumnFilters((current) => {
      const next = { ...current };
      delete next[key];
      return next;
    });
    setOpenFilterColumn((current) => (current === key ? null : current));
  }

  function closeColumnFilterMenu() {
    setOpenFilterColumn(null);
    setFilterDraftValues([]);
    setFilterValueSearch("");
  }

  function toggleColumnFilterMenu(key: CellKey) {
    if (openFilterColumn === key) {
      closeColumnFilterMenu();
      return;
    }

    const availableValues = distinctValuesByColumn[key] ?? [];
    const activeValues = columnFilters[key] ?? [];
    setFilterDraftValues(activeValues.length > 0 ? activeValues : availableValues);
    setFilterValueSearch("");
    setOpenFilterColumn(key);
  }

  function applyColumnFilter(key: CellKey) {
    const availableValues = distinctValuesByColumn[key] ?? [];
    const availableSet = new Set(availableValues);
    const selectedValues = [...new Set(filterDraftValues)].filter((value) => availableSet.has(value));
    if (selectedValues.length === 0) {
      return;
    }

    if (selectedValues.length === availableValues.length) {
      clearColumnFilter(key);
    } else {
      setColumnFilters((current) => ({ ...current, [key]: selectedValues }));
    }
    closeColumnFilterMenu();
  }

  function clearSortAndFilters() {
    setSortState(null);
    setColumnFilters({});
    closeColumnFilterMenu();
  }

  function renderActiveViewSummary() {
    if (!sortState && activeColumnFilters.length === 0) {
      return null;
    }

    const sortedColumn = sortState
      ? allColumns.find((column) => column.key === sortState.key)
      : undefined;

    return (
      <div className="active-view-summary" aria-label="Aktive Sortierung und Filter">
        <span className="active-view-label">Sortierung und Filter</span>
        {sortState ? (
          <button
            type="button"
            className="active-view-chip"
            onClick={() => setSortState(null)}
            title="Sortierung entfernen"
          >
            Sortierung: {sortedColumn?.label ?? sortState.key} ({sortState.direction === "asc" ? "aufsteigend" : "absteigend"})
            <span aria-hidden="true"> ×</span>
          </button>
        ) : null}
        {activeColumnFilters.map(({ column, values }) => {
          const shownValues = values
            .slice(0, 2)
            .map((value) =>
              column.key === "phaseType"
                ? formatPhaseTypeLabel(value)
                : value
            )
            .join(", ");
          const valueSummary = values.length > 2 ? `${shownValues} +${values.length - 2}` : shownValues;
          return (
            <button
              key={`active-filter-${column.key}`}
              type="button"
              className="active-view-chip"
              onClick={() => clearColumnFilter(column.key)}
              title={`Filter für ${column.label} entfernen`}
            >
              {column.label}: {valueSummary}
              <span aria-hidden="true"> ×</span>
            </button>
          );
        })}
        <button type="button" className="active-view-reset" onClick={clearSortAndFilters}>
          Ansicht zurücksetzen
        </button>
      </div>
    );
  }

  function renderDistributionPowerSummary() {
    if (!data) {
      return null;
    }
    return (
      <section
        className="distribution-power-summary"
        aria-label="Leistungszusammenfassung des Verteilers"
      >
        <div>
          <span>Gesamtleistung Verteiler</span>
          <strong>
            {formatValue(
              data.distributionBoardTotalPower,
              "rowTotalPower"
            )}{" "}
            kW
          </strong>
        </div>
        <div>
          <span>Verteilerweiter Gleichzeitigkeitsfaktor</span>
          <strong>
            {formatValue(
              data.distributionBoardSimultaneityFactor,
              "simultaneityFactor"
            )}
          </strong>
        </div>
        <div>
          <span>
            Gesamtleistung Verteiler unter Berücksichtigung des
            Gleichzeitigkeitsfaktors
          </span>
          <strong>
            {formatValue(
              data.distributionBoardTotalPowerWithSimultaneityFactor,
              "rowTotalPower"
            )}{" "}
            kW
          </strong>
        </div>
      </section>
    );
  }

  function closeColumnSettingsMenu() {
    setIsColumnMenuOpen(false);
    setColumnSearch("");
  }

  function toggleColumnSettingsMenu() {
    setIsColumnMenuOpen((current) => {
      if (current) {
        setColumnSearch("");
      }
      return !current;
    });
  }

  function renderColumnSettingsMenu(keyPrefix: string) {
    if (!isColumnMenuOpen) {
      return null;
    }

    const search = columnSearch.trim().toLocaleLowerCase("de-DE");
    const matchingColumns = orderedColumns.filter((column) =>
      `${column.label} ${getFullColumnLabel(column)}`
        .toLocaleLowerCase("de-DE")
        .includes(search)
    );

    return (
      <div className="column-settings-menu">
        <div className="column-settings-title-row">
          <strong>Spalten auswählen</strong>
          <button type="button" className="column-settings-close" onClick={closeColumnSettingsMenu} aria-label="Spaltenmenü schließen">
            ×
          </button>
        </div>
        <div className="column-settings-explanation">
          Spalten ein- oder ausblenden. Die Reihenfolge lässt sich mit den Pfeilen oder per Ziehen ändern.
        </div>
        <input
          type="search"
          className="column-settings-search"
          value={columnSearch}
          onChange={(event) => setColumnSearch(event.target.value)}
          placeholder="Spalten suchen …"
          aria-label="Spalten suchen"
          autoFocus
        />
        <div className="column-settings-list">
          {matchingColumns.map((column) => {
            const visible = visibleColumnKeys.includes(column.key);
            return (
              <div
                key={`${keyPrefix}-${column.key}`}
                className={`column-settings-item ${visible ? "selected" : ""} ${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()}
              >
                <button
                  type="button"
                  className="column-visibility-button"
                  aria-pressed={visible}
                  disabled={column.locked}
                  onClick={() => toggleColumnVisibility(column.key)}
                >
                  <span className="selection-marker" aria-hidden="true">{visible ? "✓" : ""}</span>
                  <span>{getFullColumnLabel(column)}</span>
                </button>
                <div className="column-settings-order">
                  <button type="button" onClick={() => moveColumn(column.key, -1)} disabled={column.locked} title="Spalte nach links verschieben">
                    ←
                  </button>
                  <button type="button" onClick={() => moveColumn(column.key, 1)} disabled={column.locked} title="Spalte nach rechts verschieben">
                    →
                  </button>
                </div>
              </div>
            );
          })}
          {matchingColumns.length === 0 ? <div className="column-settings-empty">Keine passende Spalte gefunden.</div> : null}
        </div>
        <div className="column-settings-footer">
          <button type="button" onClick={() => resetColumns()}>Standard wiederherstellen</button>
          <button type="button" className="primary" onClick={closeColumnSettingsMenu}>Fertig</button>
        </div>
      </div>
    );
  }

  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 || "Stromkreis"}`,
      }))
    );
  }, [data]);

  const allCircuits = useMemo(
    () => data?.sections.flatMap((section) => section.circuits) ?? [],
    [data]
  );
  const duplicateEquipmentIdentifierCircuitIds = useMemo(
    () => new Set(findDuplicateEquipmentIdentifierCircuitIds(allCircuits)),
    [allCircuits]
  );

  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 });
    focusGridWithoutScroll();
  }

  // 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) {
      focusGridWithoutScroll();
    }
  }

  function getExpectedProjectRevision() {
    if (projectRevisionRef.current === null) {
      throw new Error("Der Projektstand ist noch nicht geladen.");
    }
    return projectRevisionRef.current;
  }

  function applyProjectCommandResult(result: ProjectCommandResultDto) {
    projectRevisionRef.current = result.history.currentRevision;
    setHistoryState(result.history);
    setData((current) =>
      current
        ? {
            ...current,
            currentRevision: result.history.currentRevision,
          }
        : current
    );
  }

  function createDeviceRowSnapshot(
    circuitId: string,
    values: CreateCircuitDeviceRowInputDto,
    defaultSortOrder: number
  ) {
    return buildCircuitDeviceRowInsertSnapshot({
      id: crypto.randomUUID(),
      circuitId,
      values,
      defaultSortOrder,
    });
  }

  function createCircuitSnapshot(
    values: CreateCircuitInputDto,
    deviceRowValues: CreateCircuitDeviceRowInputDto[] = []
  ) {
    const section = data?.sections.find(
      (entry) => entry.id === values.sectionId
    );
    if (!section || !data) {
      throw new Error("Bereich wurde nicht gefunden.");
    }
    const voltage = resolveProjectVoltage(
      resolveCircuitPhaseType(
        section.key,
        deviceRowValues.map((row) => row.phaseType)
      ),
      data
    );
    const circuitId = crypto.randomUUID();
    const deviceRows = deviceRowValues.map((row, index) =>
      createDeviceRowSnapshot(circuitId, row, (index + 1) * 10)
    );
    return buildCircuitInsertSnapshot({
      id: circuitId,
      circuitListId,
      values: { ...values, voltage },
      deviceRows,
    });
  }

  async function persistCircuitInsert(
    circuit: CircuitSnapshot,
    description: string
  ) {
    const result = await insertCircuitCommand(
      projectId,
      getExpectedProjectRevision(),
      circuit,
      description
    );
    applyProjectCommandResult(result);
  }

  async function persistCircuitDelete(
    circuitId: string,
    description: string
  ) {
    const result = await deleteCircuitCommand(
      projectId,
      getExpectedProjectRevision(),
      circuitId,
      circuitListId,
      description
    );
    applyProjectCommandResult(result);
  }

  async function persistDeviceRowInsert(
    row: CircuitDeviceRowSnapshot,
    description: string
  ) {
    const result = await insertCircuitDeviceRowCommand(
      projectId,
      getExpectedProjectRevision(),
      row,
      description
    );
    applyProjectCommandResult(result);
  }

  async function persistDeviceRowDelete(
    rowId: string,
    circuitId: string,
    description: string
  ) {
    const result = await deleteCircuitDeviceRowCommand(
      projectId,
      getExpectedProjectRevision(),
      rowId,
      circuitId,
      description
    );
    applyProjectCommandResult(result);
  }

  // Runs a normal command. The server records it in project-wide history.
  async function runCommand(command: HistoryCommand) {
    try {
      setError(null);
      setIsSaving(true);
      const intent = await command.redo();
      await reloadWithIntent(intent ?? null);
    } catch (err) {
      const message = normalizeUiError(err);
      await loadTree({ showLoading: false });
      setError(message);
    } finally {
      setIsSaving(false);
    }
  }

  // Applies the next eligible project-wide history operation. Selection is only
  // a best-effort local hint; command eligibility and data changes stay server-owned.
  async function applyHistory(mode: "undo" | "redo") {
    try {
      setError(null);
      setHistoryBusy(true);
      setIsSaving(true);
      const intent = selectedCell ? buildSelectionIntent(selectedCell) : null;
      const result =
        mode === "undo"
          ? await undoProjectCommand(
              projectId,
              getExpectedProjectRevision()
            )
          : await redoProjectCommand(
              projectId,
              getExpectedProjectRevision()
            );
      applyProjectCommandResult(result);
      await Promise.all([
        reloadWithIntent(intent),
        loadProjectDeviceList(),
      ]);
    } catch (err) {
      const message = normalizeUiError(err);
      await loadTree({ showLoading: false });
      setError(message);
    } finally {
      setIsSaving(false);
      setHistoryBusy(false);
    }
  }

  async function handleUndo() {
    if (historyBusy || isSaving || !historyState || historyState.undoDepth === 0) {
      return;
    }
    await applyHistory("undo");
  }

  async function handleSaveDistributionBoardComponent(
    values: DistributionBoardComponentEditorValues
  ) {
    const intent = componentEditorIntent;
    if (!intent || !data) {
      return;
    }
    await runCommand({
      label:
        intent.kind === "edit"
          ? "Verteilerkomponente bearbeiten"
          : "Verteilerkomponente hinzufügen",
      redo: async () => {
        const result =
          intent.kind === "edit"
            ? await (() => {
                const expected = toDistributionBoardComponentSnapshot(
                  intent.component
                );
                return updateDistributionBoardComponentCommand(
                  projectId,
                  getExpectedProjectRevision(),
                  expected,
                  updateDistributionBoardComponentSnapshot(expected, values)
                );
              })()
            : await (() => {
                const components =
                  intent.role === "auxiliary"
                    ? data.footerComponents
                    : data.sections.find(
                        (section) => section.id === intent.sectionId
                      )?.components ?? [];
                const snapshot = buildDistributionBoardComponentSnapshot({
                  id: crypto.randomUUID(),
                  circuitListId,
                  role: intent.role,
                  sectionId: intent.sectionId,
                  sortOrder: getNextComponentSortOrder(components),
                  values,
                });
                return insertDistributionBoardComponentCommand(
                  projectId,
                  getExpectedProjectRevision(),
                  snapshot
                );
              })();
        applyProjectCommandResult(result);
        setComponentEditorIntent(null);
        return null;
      },
    });
  }

  async function handleDeleteDistributionBoardComponent(
    component: CircuitTreeComponentDto
  ) {
    if (
      !confirm(
        `Verteilerkomponente „${component.equipmentIdentifier} ${component.name}“ entfernen?`
      )
    ) {
      return;
    }
    await runCommand({
      label: "Verteilerkomponente entfernen",
      redo: async () => {
        const result = await deleteDistributionBoardComponentCommand(
          projectId,
          getExpectedProjectRevision(),
          toDistributionBoardComponentSnapshot(component)
        );
        applyProjectCommandResult(result);
        setComponentEditorIntent(null);
        return null;
      },
    });
  }

  async function handleSaveCircuitGroup(values: {
    category: CircuitGroupCategory;
    displayName: string;
  }) {
    const intent = circuitGroupEditorIntent;
    if (!intent || !data) {
      return;
    }
    await runCommand({
      label:
        intent.kind === "create"
          ? "Stromkreisgruppe hinzufügen"
          : "Stromkreisgruppe bearbeiten",
      redo: async () => {
        const result =
          intent.kind === "create"
            ? await insertCircuitGroupCommand(
                projectId,
                getExpectedProjectRevision(),
                buildNewCircuitGroupSnapshot({
                  id: crypto.randomUUID(),
                  circuitListId,
                  category: values.category,
                  displayName: values.displayName,
                  sections: data.sections,
                })
              )
            : await (() => {
                const expected = toCircuitGroupSnapshot(
                  intent.section,
                  circuitListId
                );
                return updateCircuitGroupCommand(
                  projectId,
                  getExpectedProjectRevision(),
                  expected,
                  renameCircuitGroupSnapshot(expected, values.displayName)
                );
              })();
        applyProjectCommandResult(result);
        setCircuitGroupEditorIntent(null);
        return null;
      },
    });
  }

  async function handleDeleteEmptyCircuitGroup(
    section: CircuitTreeResponseDto["sections"][number]
  ) {
    if (!canDeleteCircuitGroup(section)) {
      return;
    }
    if (!confirm(`Leere Stromkreisgruppe „${section.displayName}“ entfernen?`)) {
      return;
    }
    await runCommand({
      label: "Leere Stromkreisgruppe entfernen",
      redo: async () => {
        const result = await deleteCircuitGroupCommand(
          projectId,
          getExpectedProjectRevision(),
          toCircuitGroupSnapshot(section, circuitListId)
        );
        applyProjectCommandResult(result);
        setCircuitGroupEditorIntent(null);
        return null;
      },
    });
  }

  async function handleRedo() {
    if (historyBusy || isSaving || !historyState || historyState.redoDepth === 0) {
      return;
    }
    await applyHistory("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;
    }

    const reorderedSections = changedSections.map((changedSection) => {
      const currentSection = data.sections.find(
        (section) => section.id === changedSection.sectionId
      );
      if (!currentSection) {
        throw new Error("Ein sortierter Bereich wurde nicht gefunden.");
      }
      return {
        sectionId: currentSection.id,
        assignments: buildCircuitSectionReorderAssignments(
          currentSection.circuits,
          changedSection.order
        ),
      };
    });

    // Sorting is view-only until explicitly applied. All affected sections are
    // then committed as one atomic project-history entry.
    await runCommand({
      label: "Sortierte Reihenfolge übernehmen",
      redo: async () => {
        const result = await reorderCircuitSectionsCommand(
          projectId,
          getExpectedProjectRevision(),
          reorderedSections,
          "Sortierte Reihenfolge übernehmen"
        );
        applyProjectCommandResult(result);
        setSortState(null);
        setOpenFilterColumn(null);
        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);
    }
    focusGridWithoutScroll();
  }, [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 (
        inputRef.current instanceof HTMLInputElement &&
        editingCell.mode === "selectExisting"
      ) {
        inputRef.current.select();
      } else if (inputRef.current instanceof HTMLInputElement) {
        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);
  }

  function focusGridWithoutScroll() {
    requestAnimationFrame(() => containerRef.current?.focus({ preventScroll: true }));
  }

  // 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" && cell.cellKey !== "phaseType"
        ? 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 grid edits to typed persistent project commands.
  async function patchCircuit(circuitId: string, key: CellKey, draft: string) {
    const result = await updateCircuitById(
      projectId,
      getExpectedProjectRevision(),
      circuitId,
      buildCircuitEditPatch(key, draft)
    );
    applyProjectCommandResult(result);
  }

  // Device-row commands derive local ProjectDevice override metadata server-side.
  async function patchDeviceRow(rowId: string, key: CellKey, draft: string) {
    const result = await updateCircuitDeviceRowById(
      projectId,
      getExpectedProjectRevision(),
      rowId,
      buildDeviceRowEditPatch(key, draft)
    );
    applyProjectCommandResult(result);
  }

  // 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("Bereich wurde nicht gefunden.");
    }
    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);

    if (isDeviceField) {
      const editValues = createValuesFromEditPatch(
        buildDeviceRowEditPatch(key, draft)
      );
      const circuit = createCircuitSnapshot(
        {
          sectionId,
          equipmentIdentifier: next.nextIdentifier,
          displayName: "Neuer Stromkreis",
          sortOrder,
          isReserve: false,
        },
        [
          {
            name: "Manuelles Gerät",
            displayName: "Manuelles Gerät",
            phaseType: "single_phase",
            quantity: 1,
            powerPerUnit: 0,
            simultaneityFactor: 1,
            cosPhi: 1,
            ...editValues,
          },
        ]
      );
      await persistCircuitInsert(circuit, "Aus freier Zeile erstellen");
      return { rowKey: `circuitCompact:${circuit.id}`, cellKey: key };
    }

    const editValues = createValuesFromEditPatch(
      buildCircuitEditPatch(key, draft)
    );
    const circuit = createCircuitSnapshot({
      sectionId,
      equipmentIdentifier: next.nextIdentifier,
      displayName: "Neuer Stromkreis",
      sortOrder,
      isReserve: true,
      ...editValues,
    });
    await persistCircuitInsert(circuit, "Aus freier Zeile erstellen");
    return { rowKey: `reserveCircuit:${circuit.id}`, cellKey: key };
  }

  // Commits the active editor input through command history so edits remain undoable.
  async function commitEdit(
    direction: SaveDirection = "stay",
    draftOverride?: string
  ) {
    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;
    }
    const draft = draftOverride ?? editingCell.draft;
    if (
      editingCell.cellKey === "equipmentIdentifier" &&
      row.circuit &&
      hasEquipmentIdentifierConflict(allCircuits, row.circuit.id, draft)
    ) {
      setError("Dieses Betriebsmittelkennzeichen ist bereits vorhanden. Die Änderung wurde nicht gespeichert.");
      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") {
      const command: HistoryCommand = {
        label: "Aus freier Zeile erstellen",
        redo: async () => {
          const selection = await createFromPlaceholder(
            row.sectionId,
            editingCell.cellKey,
            draft
          );
          targetSelection = selection;
          return nextSelectionIntent();
        },
      };
      setEditingCell(null);
      await runCommand(command);
      return;
    }

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

    if (cell.kind === "deviceField" && row.device) {
      const next = draft;
      const command: HistoryCommand = {
        label: "Gerätefeld bearbeiten",
        redo: async () => {
          await patchDeviceRow(row.device!.id, editingCell.cellKey, next);
          return nextSelectionIntent();
        },
      };
      setEditingCell(null);
      await runCommand(command);
      return;
    }

    if (cell.kind === "deviceField" && row.rowType === "reserveCircuit" && row.circuit) {
      const next = draft;
      const command: HistoryCommand = {
        label: "Gerätezeile im Reservestromkreis erstellen",
        redo: async () => {
          const editValues = createValuesFromEditPatch(
            buildDeviceRowEditPatch(editingCell.cellKey, next)
          );
          const snapshot = createDeviceRowSnapshot(
            row.circuit!.id,
            {
              name: "Reserveverbraucher",
              displayName: "Reserveverbraucher",
              phaseType: "single_phase",
              quantity: 1,
              powerPerUnit: 0,
              simultaneityFactor: 1,
              cosPhi: 1,
              ...editValues,
            },
            10
          );
          await persistDeviceRowInsert(
            snapshot,
            "Gerätezeile im Reservestromkreis erstellen"
          );
          targetSelection = { rowKey: `circuitCompact:${row.circuit!.id}`, cellKey: editingCell.cellKey };
          return nextSelectionIntent();
        },
      };
      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 });
    focusGridWithoutScroll();
  }

  async function handleAddReserveCircuit(sectionId: string, afterCircuitId?: string) {
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      return;
    }
    await runCommand({
      label: "Stromkreis hinzufügen",
      redo: async () => {
        const next = await getNextCircuitIdentifier(sectionId);
        const sortOrder = getInsertionSortOrder(section.circuits, afterCircuitId);
        const circuit = createCircuitSnapshot({
          sectionId,
          equipmentIdentifier: next.nextIdentifier,
          displayName: "Reserve",
          sortOrder,
          isReserve: true,
        });
        await persistCircuitInsert(circuit, "Stromkreis hinzufügen");
        setActiveSectionId(sectionId);
        return {
          rowKey: `reserveCircuit:${circuit.id}`,
          cellKey: "equipmentIdentifier",
          rowType: "reserveCircuit",
          sectionId,
          circuitId: circuit.id,
        };
      },
    });
  }

  async function handleAddManualDevice(
    circuit: CircuitTreeCircuitDto,
    sectionId: string,
    afterDeviceRowId?: string
  ) {
    const originalDeviceCount = circuit.deviceRows.length;
    const section = data?.sections.find((entry) => entry.id === sectionId);
    await runCommand({
      label: "Manuelles Gerät hinzufügen",
      redo: async () => {
        const rowSnapshot = createDeviceRowSnapshot(circuit.id, {
          name: "Manuelles Gerät",
          displayName: "Manuelles Gerät",
          phaseType: section?.key === "three_phase" ? "three_phase" : "single_phase",
          quantity: 1,
          powerPerUnit: 0,
          simultaneityFactor: 1,
          cosPhi: 1,
          sortOrder: getInsertionSortOrder(circuit.deviceRows, afterDeviceRowId),
        }, 10);
        await persistDeviceRowInsert(
          rowSnapshot,
          "Manuelles Gerät hinzufügen"
        );
        setActiveSectionId(sectionId);
        return {
          rowKey: originalDeviceCount === 0 ? `circuitCompact:${circuit.id}` : `device:${rowSnapshot.id}`,
          cellKey: "displayName",
          rowType: originalDeviceCount === 0 ? "circuitCompact" : "deviceRow",
          sectionId,
          circuitId: circuit.id,
          deviceId: rowSnapshot.id,
        };
      },
    });
  }

  async function handleInsertBelowSelection() {
    const row = selectedCell ? findRow(selectedCell.rowKey) : null;
    const cell = row?.cells.find((entry) => entry.cellKey === selectedCell?.cellKey);
    const selection =
      row && cell && isGridInsertionRowType(row.rowType)
        ? {
            rowType: row.rowType,
            cellKind: cell.kind,
            sectionId: row.sectionId,
            circuitId: row.circuit?.id,
            deviceId: row.device?.id,
          }
        : null;
    const intent = resolveGridInsertionIntent(
      selection,
      activeSectionId ?? data?.sections[0]?.id
    );
    if (!intent) {
      setError("Zum Einfügen ist kein Bereich verfügbar.");
      return;
    }
    if (intent.kind === "circuit") {
      await handleAddReserveCircuit(intent.sectionId, intent.afterCircuitId);
      return;
    }
    const circuit = data?.sections
      .flatMap((sectionEntry) => sectionEntry.circuits)
      .find((entry) => entry.id === intent.circuitId);
    if (!circuit) {
      setError("Der Stromkreis ist ungültig.");
      return;
    }
    await handleAddManualDevice(circuit, intent.sectionId, intent.afterDeviceRowId);
  }

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

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

  function isProjectDeviceTargetValid(device: ProjectDeviceDto, sectionId: string) {
    const section = data?.sections.find((entry) => entry.id === sectionId);
    return Boolean(section && isProjectDevicePlacementValid(device, section));
  }

  function getProjectDeviceTargetError(device: ProjectDeviceDto, sectionId: string) {
    const target = data?.sections.find((entry) => entry.id === sectionId);
    const expectedKey = inferProjectDeviceSectionKey(device);
    const expected = data?.sections.find((entry) => entry.key === expectedKey);
    if (!target) {
      return "Der Zielbereich ist ungültig.";
    }
    return `${device.displayName || device.name} gehört in den Bereich ${expected?.displayName ?? expectedKey}, nicht in ${target.displayName}.`;
  }

  async function handleAddProjectDeviceAsNewCircuit() {
    const device = resolveSelectedProjectDevice();
    if (!device) {
      setError("Bitte ein Projektgerät auswählen.");
      return;
    }
    if (!targetSectionId) {
      setError("Bitte einen Zielbereich auswählen.");
      return;
    }
    if (!isProjectDeviceTargetValid(device, targetSectionId)) {
      setError(getProjectDeviceTargetError(device, targetSectionId));
      return;
    }
    await runCommand({
      label: "Projektgerät als neuen Stromkreis hinzufügen",
      redo: async () => {
        const created = await insertProjectDeviceAsNewCircuit(device, targetSectionId);
        return {
          rowKey: `device:${created.rowId}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId: targetSectionId,
          circuitId: created.circuitId,
          deviceId: created.rowId,
        };
      },
    });
  }

  async function handleAddProjectDeviceToCircuit() {
    const device = resolveSelectedProjectDevice();
    if (!device) {
      setError("Bitte ein Projektgerät auswählen.");
      return;
    }
    let circuitId = targetCircuitId;
    if (!circuitId && selectedCell) {
      const row = findRow(selectedCell.rowKey);
      if (row?.circuit?.id) {
        circuitId = row.circuit.id;
      }
    }
    if (!circuitId) {
      setError("Bitte einen Zielstromkreis auswählen.");
      return;
    }
    const sectionId = findCircuitSectionId(circuitId);
    if (!sectionId || !isProjectDeviceTargetValid(device, sectionId)) {
      setError(getProjectDeviceTargetError(device, sectionId ?? ""));
      return;
    }

    await runCommand({
      label: "Projektgerät zum Stromkreis hinzufügen",
      redo: async () => {
        const created = await insertProjectDeviceToCircuit(device, circuitId);
        return {
          rowKey: `device:${created.rowId}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId: findCircuitSectionId(circuitId) ?? "",
          circuitId,
          deviceId: created.rowId,
        };
      },
    });
  }

  async function insertProjectDeviceAsNewCircuit(device: ProjectDeviceDto, sectionId: string) {
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      throw new Error("Der Zielbereich ist ungültig.");
    }
    const next = await getNextCircuitIdentifier(sectionId);
    const sortOrder =
      section.circuits.length > 0 ? Math.max(...section.circuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
    const circuit = createCircuitSnapshot(
      {
        sectionId,
        equipmentIdentifier: next.nextIdentifier,
        displayName: device.displayName || device.name,
        sortOrder,
        isReserve: false,
      },
      [
        {
          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,
        },
      ]
    );
    await persistCircuitInsert(
      circuit,
      "Projektgerät als neuen Stromkreis hinzufügen"
    );
    const createdRow = circuit.deviceRows[0];

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

  async function insertProjectDeviceToCircuit(device: ProjectDeviceDto, circuitId: string) {
    const circuit = allCircuits.find((entry) => entry.id === circuitId);
    if (!circuit) {
      throw new Error("Der Zielstromkreis ist ungültig.");
    }
    const row = createDeviceRowSnapshot(
      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,
      },
      getInsertionSortOrder(circuit.deviceRows)
    );
    await persistDeviceRowInsert(
      row,
      "Projektgerät zum Stromkreis hinzufügen"
    );
    return { rowId: row.id };
  }

  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;
  }

  function getDeviceRowSourceSectionIds(rowIds: string[]) {
    const sectionIds = new Set<string>();
    for (const rowId of rowIds) {
      const circuitId = findDeviceRowCircuitId(rowId);
      const sectionId = circuitId ? findCircuitSectionId(circuitId) : null;
      if (sectionId) {
        sectionIds.add(sectionId);
      }
    }
    return [...sectionIds];
  }

  function confirmCrossSectionDeviceMove(rowIds: string[], targetSectionId: string) {
    const sourceSectionIds = getDeviceRowSourceSectionIds(rowIds);
    if (!requiresCrossSectionMoveConfirmation(sourceSectionIds, targetSectionId)) {
      return true;
    }
    const sourceNames = sourceSectionIds
      .map((sectionId) => data?.sections.find((section) => section.id === sectionId)?.displayName ?? sectionId)
      .join(", ");
    const targetName =
      data?.sections.find((section) => section.id === targetSectionId)?.displayName ?? targetSectionId;
    return confirm(
      `${rowIds.length} Gerätezeile(n) von ${sourceNames} nach ${targetName} verschieben?\n\n` +
        "Dabei wird eine Bereichsgrenze überschritten. Phasenart, Kategorie, Projektgeräte-Verknüpfungen und lokale Werte bleiben unverändert. " +
        "Die technische und nummerische Zuordnung im Zielbereich muss daher möglicherweise manuell geprüft werden.\n\n" +
        "Bestehende Betriebsmittelkennzeichen werden nicht neu nummeriert."
    );
  }

  // Applies same-section circuit reorder as explicit id ordering.
  // No implicit renumbering is performed here.
  function resolveCircuitReorderOrder(intent: CircuitReorderDropIntent, sourceCircuitIds: string[]) {
    const section = data?.sections.find((entry) => entry.id === intent.sectionId);
    if (!section) {
      throw new Error("Der Bereich ist ungültig.");
    }
    const ids = section.circuits.map((circuit) => circuit.id);
    const block = sourceCircuitIds.filter((id) => ids.includes(id));
    if (block.length === 0) {
      throw new Error("Der Quellstromkreis ist ungültig.");
    }
    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("Der Zielstromkreis ist ungültig.");
      }
      const insertIndex = intent.kind === "after-circuit" ? targetIndex + 1 : targetIndex;
      nextIds.splice(insertIndex, 0, ...block);
    }
    return 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("Das gezogene Projektgerät fehlt.");
      return;
    }
    const device = projectDevices.find((entry) => entry.id === draggedId);
    if (!device) {
      setError("Die Quelle des gezogenen Projektgeräts ist ungültig.");
      return;
    }
    if (!intent.valid || !isProjectDeviceTargetValid(device, intent.sectionId)) {
      setError(getProjectDeviceTargetError(device, intent.sectionId));
      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") {
      await runCommand({
        label: "Projektgerät in neuen Stromkreis ziehen",
        redo: async () => {
          const created = await insertProjectDeviceAsNewCircuit(device, intent.sectionId);
          return {
            rowKey: `device:${created.rowId}`,
            cellKey: "displayName",
            rowType: "deviceRow",
            sectionId: intent.sectionId,
            circuitId: created.circuitId,
            deviceId: created.rowId,
          };
        },
      });
      return;
    }
    await runCommand({
      label: "Projektgerät in Stromkreis ziehen",
      redo: async () => {
        const created = await insertProjectDeviceToCircuit(device, intent.circuitId);
        return {
          rowKey: `device:${created.rowId}`,
          cellKey: "displayName",
          rowType: "deviceRow",
          sectionId: intent.sectionId,
          circuitId: intent.circuitId,
          deviceId: created.rowId,
        };
      },
    });
  }

  // Handles single and bulk device-row drag through persistent project history.
  // Exact source and target positions make undo deterministic.
  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("Die gezogene Gerätezeile fehlt.");
      return;
    }

    const sourceByRowId = new Map<string, string>();
    for (const rowId of rowIds) {
      const sourceCircuitId = findDeviceRowCircuitId(rowId);
      if (!sourceCircuitId) {
        setError("Die gezogene Gerätezeile ist ungültig.");
        return;
      }
      sourceByRowId.set(rowId, sourceCircuitId);
    }
    const sourceCircuitId = sourceByRowId.get(rowIds[0]);
    if (!sourceCircuitId) {
      setError("Die Quelle der gezogenen Gerätezeile ist ungültig.");
      return;
    }

    if (intent.kind === "move-to-circuit" && rowIds.length === 1 && intent.circuitId === sourceCircuitId) {
      return;
    }
    if (!confirmCrossSectionDeviceMove(rowIds, intent.sectionId)) {
      return;
    }

    const circuits = (data?.sections ?? []).flatMap(
      (section) => section.circuits
    );
    const label =
      rowIds.length > 1
        ? `${rowIds.length} Gerätezeilen verschieben`
        : "Gerätezeile verschieben";

    if (intent.kind === "move-to-circuit") {
      const targetCircuit = circuits.find(
        (circuit) => circuit.id === intent.circuitId
      );
      if (!targetCircuit) {
        setError("Der Zielstromkreis ist ungültig.");
        return;
      }
      const moves = buildCircuitDeviceRowMoveAssignments({
        circuits,
        rowIds,
        targetCircuitId: targetCircuit.id,
        targetDeviceRows: targetCircuit.deviceRows,
      });
      if (moves.length === 0) {
        return;
      }
      await runCommand({
        label,
        redo: async () => {
          const result = await moveCircuitDeviceRowsCommand(
            projectId,
            getExpectedProjectRevision(),
            moves,
            label
          );
          applyProjectCommandResult(result);
          pendingSelectedDeviceRowIdsAfterReload.current = rowIds;
          return null;
        },
      });
      return;
    }

    const targetSection = data?.sections.find(
      (section) => section.id === intent.sectionId
    );
    if (!targetSection) {
      setError("Der Zielbereich ist ungültig.");
      return;
    }
    const newCircuitLabel =
      rowIds.length > 1
        ? `${rowIds.length} Gerätezeilen in neuen Stromkreis verschieben`
        : "Gerätezeile in neuen Stromkreis verschieben";
    await runCommand({
      label: newCircuitLabel,
      redo: async () => {
        const next = await getNextCircuitIdentifier(intent.sectionId);
        const sortOrder =
          targetSection.circuits.length > 0
            ? Math.max(
                ...targetSection.circuits.map(
                  (circuit) => circuit.sortOrder
                )
              ) + 10
            : 10;
        const targetCircuit = createCircuitSnapshot({
          sectionId: intent.sectionId,
          equipmentIdentifier: next.nextIdentifier,
          displayName: "Neuer Stromkreis",
          sortOrder,
          isReserve: true,
        });
        const moves = buildCircuitDeviceRowMoveAssignments({
          circuits,
          rowIds,
          targetCircuitId: targetCircuit.id,
          targetDeviceRows: [],
        });
        const result =
          await moveCircuitDeviceRowsToNewCircuitCommand(
            projectId,
            getExpectedProjectRevision(),
            targetCircuit,
            moves,
            newCircuitLabel
          );
        applyProjectCommandResult(result);
        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("Der gezogene Stromkreis fehlt.");
      return;
    }
    if (!intent.valid) {
      setError("Stromkreise können derzeit nicht bereichsübergreifend verschoben werden.");
      return;
    }
    const section = data?.sections.find((entry) => entry.id === intent.sectionId);
    if (!section) {
      setError("Der Bereich ist ungültig.");
      return;
    }
    const sectionCircuitIds = new Set(section.circuits.map((circuit) => circuit.id));
    if (sourceCircuitIds.some((id) => !sectionCircuitIds.has(id))) {
      setError("Stromkreise können derzeit nicht bereichsübergreifend verschoben werden.");
      return;
    }
    const primaryCircuitId = sourceCircuitIds[0];
    const targetOrder = resolveCircuitReorderOrder(
      intent,
      sourceCircuitIds
    );
    const assignments = buildCircuitSectionReorderAssignments(
      section.circuits,
      targetOrder
    );
    if (assignments.length === 0) {
      return;
    }
    const label =
      sourceCircuitIds.length > 1
        ? `${sourceCircuitIds.length} Stromkreise neu anordnen`
        : "Stromkreise neu anordnen";
    const selectionIntent: SelectionIntent = {
      rowKey: `circuitSummary:${primaryCircuitId}`,
      cellKey: "equipmentIdentifier",
      rowType: "circuitSummary",
      sectionId: intent.sectionId,
      circuitId: primaryCircuitId,
    };
    await runCommand({
      label,
      redo: async () => {
        const result = await reorderCircuitSectionCommand(
          projectId,
          getExpectedProjectRevision(),
          intent.sectionId,
          assignments,
          label
        );
        applyProjectCommandResult(result);
        pendingSelectedCircuitIdsAfterReload.current = sourceCircuitIds;
        return selectionIntent;
      },
    });
  }

  // Persistent delete commands preserve the complete row and its stable id.
  async function handleDeleteDevice(rowId: string) {
    const sourceCircuitId = findDeviceRowCircuitId(rowId);
    const sourceCircuit = sourceCircuitId
      ? allCircuits.find((circuit) => circuit.id === sourceCircuitId)
      : null;
    const rowSnapshot = sourceCircuit?.deviceRows.find((row) => row.id === rowId);
    if (!sourceCircuitId || !sourceCircuit || !rowSnapshot) {
      setError("Die Gerätezeile ist ungültig.");
      return;
    }
    if (sourceCircuit.deviceRows.length === 1) {
      const keepAsReserve = confirm(
        `„${rowSnapshot.displayName}“ ist das letzte Gerät in ${sourceCircuit.equipmentIdentifier}.\n\n` +
          "OK: Gerät löschen und den leeren Stromkreis als Reserve behalten.\n" +
          "Abbrechen: anschließend entscheiden, ob stattdessen der gesamte Stromkreis gelöscht werden soll."
      );
      if (!keepAsReserve) {
        const deleteCircuit = confirm(
          `Den vollständigen Stromkreis ${sourceCircuit.equipmentIdentifier} löschen?\n\n` +
            "OK: Stromkreis und Gerät löschen.\nAbbrechen: nichts löschen."
        );
        if (deleteCircuit) {
          await handleDeleteCircuit(sourceCircuitId, false);
        }
        return;
      }
    } else if (!confirm(`Gerätezeile „${rowSnapshot.displayName}“ löschen?`)) {
      return;
    }
    await runCommand({
      label: "Gerätezeile löschen",
      redo: async () => {
        await persistDeviceRowDelete(
          rowId,
          sourceCircuitId,
          "Gerätezeile löschen"
        );
        return null;
      },
    });
  }

  // Persistent circuit delete preserves the complete circuit block and all stable ids.
  async function handleDeleteCircuit(circuitId: string, askForConfirmation = true) {
    const circuit = allCircuits.find((entry) => entry.id === circuitId);
    if (!circuit) {
      setError("Der Stromkreis ist ungültig.");
      return;
    }
    if (
      askForConfirmation &&
      !confirm(
        `Stromkreis ${circuit.equipmentIdentifier} und ${circuit.deviceRows.length} zugeordnete Gerätezeile(n) löschen?\n\n` +
          "Bestehende Betriebsmittelkennzeichen werden nicht neu nummeriert."
      )
    ) {
      return;
    }
    await runCommand({
      label: "Stromkreis löschen",
      redo: async () => {
        await persistCircuitDelete(circuitId, "Stromkreis löschen");
        return null;
      },
    });
  }

  async function handleDeleteSelection() {
    const row = selectedCell ? findRow(selectedCell.rowKey) : null;
    const cell = row?.cells.find((entry) => entry.cellKey === selectedCell?.cellKey);
    const intent = resolveGridDeleteIntent(
      row && cell && isGridInsertionRowType(row.rowType)
        ? {
            rowType: row.rowType,
            cellKind: cell.kind,
            circuitId: row.circuit?.id,
            deviceId: row.device?.id,
          }
        : null
    );
    if (!intent) {
      return;
    }
    if (intent.kind === "device") {
      await handleDeleteDevice(intent.deviceId);
      return;
    }
    await handleDeleteCircuit(intent.circuitId);
  }

  // Explicit renumber action through project history. Sorting and device rows
  // remain unchanged; the server handles collision-safe identifier swaps.
  async function handleRenumberSection(sectionId: string) {
    if (!confirm("Diesen Bereich neu nummerieren? Nur die Stromkreise in diesem Bereich werden geändert.")) {
      return;
    }
    const section = data?.sections.find((entry) => entry.id === sectionId);
    if (!section) {
      return;
    }
    const assignments = buildCircuitSectionRenumberAssignments(
      data?.sections ?? [],
      sectionId
    );
    if (assignments.length === 0) {
      return;
    }
    const label = "Bereich neu nummerieren";
    await runCommand({
      label,
      redo: async () => {
        const result = await renumberCircuitSectionCommand(
          projectId,
          getExpectedProjectRevision(),
          sectionId,
          assignments,
          label
        );
        applyProjectCommandResult(result);
        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;
    }
    const eventTarget = event.target as HTMLElement;
    if (eventTarget.closest("button, input, select, textarea")) {
      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();
      void handleInsertBelowSelection();
      return;
    }
    if (event.key === "Delete" && selectedCell) {
      event.preventDefault();
      void handleDeleteSelection();
      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">Stromkreislisteneditor wird geladen …</div>;
  }
  if (error && !data) {
    return <div className="notice error">{error}</div>;
  }
  if (!data || data.sections.length === 0) {
    return <div className="notice muted">Keine Bereiche oder Stromkreise vorhanden.</div>;
  }
  if (filteredSortedSections.length === 0) {
    return (
      <div className="tree-editor-shell">
        <div className="editor-toolbar">
          <button
            type="button"
            onClick={() => void handleUndo()}
            disabled={!historyState || historyState.undoDepth === 0 || historyBusy || isSaving}
          >
            Rückgängig
          </button>
          <button
            type="button"
            onClick={() => void handleRedo()}
            disabled={!historyState || historyState.redoDepth === 0 || historyBusy || isSaving}
          >
            Wiederholen
          </button>
        <button
          type="button"
          onClick={clearSortAndFilters}
          disabled={!Boolean(sortState) && !hasActiveFilters}
        >
          Sortierung/Filter zurücksetzen
        </button>
        <button type="button" onClick={toggleColumnSettingsMenu} aria-expanded={isColumnMenuOpen}>
          Spalten
        </button>
        {renderColumnSettingsMenu("col-empty")}
      </div>
        {renderActiveViewSummary()}
        {renderDistributionPowerSummary()}
        {error ? (
          <div className="notice error editor-error-notice" role="alert">
            <span>{error}</span>
            <button type="button" onClick={() => setError(null)}>Schließen</button>
          </div>
        ) : null}
        <div className="notice muted">Für die aktiven Filter wurden keine passenden Stromkreise gefunden.</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 activeDraggedDeviceRowIds =
    draggingDeviceRowIds.length > 0
      ? draggingDeviceRowIds
      : draggingDeviceRowId
        ? [draggingDeviceRowId]
        : [];
  const draggingDeviceCount = activeDraggedDeviceRowIds.length;
  const activeDraggedCircuitIds =
    draggingCircuitIds.length > 0 ? draggingCircuitIds : draggingCircuitId ? [draggingCircuitId] : [];
  const draggingCircuitCount = activeDraggedCircuitIds.length;
  const selectedProjectDevice = resolveSelectedProjectDevice();
  const suggestedSection = selectedProjectDevice
    ? data.sections.find((section) => section.key === inferProjectDeviceSectionKey(selectedProjectDevice))
    : null;
  const selectedRowCircuitId = selectedCell ? findRow(selectedCell.rowKey)?.circuit?.id ?? null : null;
  const resolvedSidebarTargetCircuitId = targetCircuitId ?? selectedRowCircuitId;
  const resolvedSidebarTargetSectionId = resolvedSidebarTargetCircuitId
    ? findCircuitSectionId(resolvedSidebarTargetCircuitId)
    : null;
  const canAddToSelectedSection = Boolean(
    selectedProjectDevice &&
      targetSectionId &&
      isProjectDeviceTargetValid(selectedProjectDevice, targetSectionId)
  );
  const canAddToSelectedCircuit = Boolean(
    selectedProjectDevice &&
      resolvedSidebarTargetSectionId &&
      isProjectDeviceTargetValid(selectedProjectDevice, resolvedSidebarTargetSectionId)
  );

  return (
    <div className="tree-editor-shell">
      {componentEditorIntent ? (
        <DistributionBoardComponentModal
          initialComponent={
            componentEditorIntent.kind === "edit"
              ? componentEditorIntent.component
              : undefined
          }
          initialEquipmentIdentifier={
            componentEditorIntent.kind === "create"
              ? componentEditorIntent.initialEquipmentIdentifier
              : undefined
          }
          isSaving={isSaving}
          onClose={() => setComponentEditorIntent(null)}
          onSave={handleSaveDistributionBoardComponent}
          role={
            componentEditorIntent.kind === "edit"
              ? (componentEditorIntent.component
                  .role as MutableDistributionBoardComponentRole)
              : componentEditorIntent.role
          }
        />
      ) : null}
      {circuitGroupEditorIntent ? (
        <CircuitGroupModal
          initialSection={
            circuitGroupEditorIntent.kind === "edit"
              ? circuitGroupEditorIntent.section
              : undefined
          }
          isSaving={isSaving}
          onClose={() => setCircuitGroupEditorIntent(null)}
          onSave={handleSaveCircuitGroup}
        />
      ) : null}
      <div className="editor-toolbar">
        <button
          type="button"
          onClick={() => void handleUndo()}
          disabled={!historyState || historyState.undoDepth === 0 || historyBusy || isSaving}
          title={
            historyState?.undoDepth
              ? `${historyState.undoDepth} Änderung(en) können rückgängig gemacht werden.`
              : undefined
          }
        >
          Rückgängig
        </button>
        <button
          type="button"
          onClick={() => void handleRedo()}
          disabled={!historyState || historyState.redoDepth === 0 || historyBusy || isSaving}
          title={
            historyState?.redoDepth
              ? `${historyState.redoDepth} Änderung(en) können wiederholt werden.`
              : undefined
          }
        >
          Wiederholen
        </button>
        {isSortedView ? (
          <button
            type="button"
            onClick={() => void handleApplySortedOrder()}
            disabled={hasActiveFilters || isSaving || historyBusy}
            title={hasActiveFilters ? "Vor dem Übernehmen der Sortierung zuerst die Filter zurücksetzen." : undefined}
          >
            Sortierung übernehmen
          </button>
        ) : null}
        <button type="button" onClick={toggleColumnSettingsMenu} aria-expanded={isColumnMenuOpen}>
          Spalten
        </button>
        <button
          aria-controls="project-device-drawer"
          aria-expanded={isProjectDeviceDrawerOpen}
          className="project-device-drawer-toggle"
          type="button"
          onClick={() =>
            setIsProjectDeviceDrawerOpen((current) => !current)
          }
        >
          {isProjectDeviceDrawerOpen
            ? "Projektgeräte schließen"
            : `Projektgeräte öffnen (${projectDevices.length})`}
        </button>
        <button
          type="button"
          disabled={isSaving || historyBusy}
          onClick={() =>
            setComponentEditorIntent({
              kind: "create",
              role: "auxiliary",
            })
          }
        >
          Verteilergerät hinzufügen
        </button>
        <button
          type="button"
          disabled={isSaving || historyBusy}
          onClick={() => setCircuitGroupEditorIntent({ kind: "create" })}
        >
          Stromkreisgruppe hinzufügen
        </button>
        <button
          type="button"
          onClick={clearSortAndFilters}
          disabled={!isSortedView && !hasActiveFilters}
        >
          Sortierung/Filter zurücksetzen
        </button>
        {renderColumnSettingsMenu("col")}
      </div>
      {renderActiveViewSummary()}
      {renderDistributionPowerSummary()}
      {hasActiveSortOrFilter ? (
        <div className="notice muted">Sortierung und Filter verändern nur die Ansicht. Die Neunummerierung ist währenddessen deaktiviert.</div>
      ) : null}
      {isSortedView && hasActiveFilters ? (
        <div className="notice muted">Die sortierte Reihenfolge kann erst nach dem Zurücksetzen der Filter übernommen werden.</div>
      ) : null}
      {error ? (
        <div className="notice error editor-error-notice" role="alert">
          <span>{error}</span>
          <button type="button" onClick={() => setError(null)}>Schließen</button>
        </div>
      ) : null}
      {duplicateEquipmentIdentifierCircuitIds.size > 0 ? (
        <div className="notice warning" role="alert">
          Doppelte Betriebsmittelkennzeichen sind hervorgehoben. Eine Neunummerierung erfolgt weiterhin nur ausdrücklich.
        </div>
      ) : null}
      {isSaving ? <div className="notice info">Wird gespeichert …</div> : null}
      <div className="tree-editor-layout">
        {isProjectDeviceDrawerOpen ? (
        <aside
          className="project-device-sidebar project-device-drawer"
          id="project-device-drawer"
        >
          <div className="project-device-drawer-header">
            <div>
              <h3>Projektgeräte</h3>
              <span>{projectDevices.length} verfügbar</span>
            </div>
            <button
              aria-label="Projektgeräte schließen"
              disabled={draggingProjectDeviceId !== null}
              onClick={() => setIsProjectDeviceDrawerOpen(false)}
              type="button"
            >
              Schließen
            </button>
          </div>
          <input
            type="text"
            placeholder="Name oder Anzeigename suchen …"
            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>Phasenart: {formatPhaseTypeLabel(device.phaseType)}</span>
                <span>Anzahl: {formatValue(device.quantity, "quantity")}</span>
                <span>
                  Leistung/Gerät: {formatValue(device.powerPerUnit, "powerPerUnit")} kW
                </span>
                <span>
                  Gleichzeitigkeit:{" "}
                  {formatValue(device.simultaneityFactor, "simultaneityFactor")}
                </span>
                <span>
                  Gesamtleistung: {formatValue(device.totalPower, "rowTotalPower")} kW
                </span>
                <span>Kostengruppe: {device.costGroup || "-"}</span>
                <span>Kategorie: {device.category || "-"}</span>
              </button>
            ))}
            {searchableProjectDevices.length === 0 ? <p className="notice muted">Keine passenden Projektgeräte gefunden.</p> : null}
          </div>
          <div className="sidebar-actions">
            <label>
              Zielbereich
              <select
                value={targetSectionId ?? ""}
                onChange={(event) => setTargetSectionId(event.target.value || null)}
              >
                <option value="">Bereich auswählen …</option>
                {data.sections.map((section) => {
                  const valid = !selectedProjectDevice || isProjectDevicePlacementValid(selectedProjectDevice, section);
                  return (
                    <option key={section.id} value={section.id} disabled={!valid}>
                      {getCircuitSectionLabel(section)}
                      {valid ? "" : " (nicht zulässig)"}
                    </option>
                  );
                })}
              </select>
            </label>
            {selectedProjectDevice && suggestedSection ? (
              <p className="notice muted">
                Vorgeschlagener Bereich:{" "}
                {getCircuitSectionLabel(suggestedSection)}
              </p>
            ) : null}
            <button
              type="button"
              disabled={!canAddToSelectedSection}
              onClick={() => void handleAddProjectDeviceAsNewCircuit()}
            >
              Als neuen Stromkreis hinzufügen
            </button>
            <label>
              Zielstromkreis
              <select
                value={targetCircuitId ?? ""}
                onChange={(event) => setTargetCircuitId(event.target.value || null)}
              >
                <option value="">Stromkreis der ausgewählten Zeile verwenden …</option>
                {circuitOptions.map((option) => {
                  const valid = !selectedProjectDevice || isProjectDeviceTargetValid(selectedProjectDevice, option.sectionId);
                  return (
                    <option key={option.id} value={option.id} disabled={!valid}>
                      {option.label}{valid ? "" : " (nicht zulässig)"}
                    </option>
                  );
                })}
              </select>
            </label>
            <button
              type="button"
              disabled={!canAddToSelectedCircuit}
              onClick={() => void handleAddProjectDeviceToCircuit()}
            >
              Zum ausgewählten Stromkreis hinzufügen
            </button>
          </div>
        </aside>
        ) : null}
        <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"
                      aria-label={`${getFullColumnLabel(column)} sortieren`}
                      title={`${getFullColumnLabel(column)} – zum Sortieren klicken`}
                      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;
                        });
                      }}
                    >
                      <span>{column.label}</span>
                      {sortState?.key === column.key ? (
                        <span className="sort-indicator" aria-label={sortState.direction === "asc" ? "aufsteigend" : "absteigend"}>
                          {sortState.direction === "asc" ? "↑" : "↓"}
                        </span>
                      ) : null}
                    </button>
                    <button
                      type="button"
                      className={`header-filter-btn ${(columnFilters[column.key]?.length ?? 0) > 0 ? "active" : ""}`}
                      onClick={() => toggleColumnFilterMenu(column.key)}
                      aria-pressed={(columnFilters[column.key]?.length ?? 0) > 0}
                      title={
                        (columnFilters[column.key]?.length ?? 0) > 0
                          ? `${columnFilters[column.key]?.length} Filterwert(e) ausgewählt`
                          : `${getFullColumnLabel(column)} filtern`
                      }
                    >
                      Filtern{(columnFilters[column.key]?.length ?? 0) > 0 ? ` (${columnFilters[column.key]?.length})` : ""}
                    </button>
                  </div>
                  {openFilterColumn === column.key ? (
                    <div className="header-filter-menu">
                      <div className="header-filter-title-row">
                        <strong>{getFullColumnLabel(column)} filtern</strong>
                        <button type="button" className="header-filter-close" onClick={closeColumnFilterMenu} aria-label="Filter schließen">
                          ×
                        </button>
                      </div>
                      <div className="header-filter-explanation">
                        Werte auswählen, die sichtbar bleiben sollen. Die Änderung wird erst beim Anwenden übernommen.
                      </div>
                      <input
                        type="search"
                        className="header-filter-search"
                        value={filterValueSearch}
                        onChange={(event) => setFilterValueSearch(event.target.value)}
                        placeholder="Werte suchen …"
                        aria-label={`Werte für ${getFullColumnLabel(column)} suchen`}
                        autoFocus
                      />
                      <div className="header-filter-selection-actions">
                        <button
                          type="button"
                          onClick={() => setFilterDraftValues(distinctValuesByColumn[column.key] ?? [])}
                        >
                          Alle auswählen
                        </button>
                        <button type="button" onClick={() => setFilterDraftValues([])}>
                          Keine auswählen
                        </button>
                        <span>{filterDraftValues.length} / {(distinctValuesByColumn[column.key] ?? []).length}</span>
                      </div>
                      <div className="header-filter-values">
                        {(distinctValuesByColumn[column.key] ?? [])
                          .filter((value) =>
                            (column.key === "phaseType"
                              ? formatPhaseTypeLabel(value)
                              : value
                            )
                              .toLocaleLowerCase("de-DE")
                              .includes(
                                filterValueSearch
                                  .trim()
                                  .toLocaleLowerCase("de-DE")
                              )
                          )
                          .map((value) => {
                            const selected = filterDraftValues.includes(value);
                            return (
                              <button
                                key={`${column.key}-${value}`}
                                type="button"
                                className={`header-filter-item ${selected ? "selected" : ""}`}
                                aria-pressed={selected}
                                onClick={() =>
                                  setFilterDraftValues((current) =>
                                    selected
                                      ? current.filter((entry) => entry !== value)
                                      : [...current, value]
                                  )
                                }
                              >
                                <span className="selection-marker" aria-hidden="true">{selected ? "✓" : ""}</span>
                                <span>
                                  {column.key === "phaseType"
                                    ? formatPhaseTypeLabel(value)
                                    : value}
                                </span>
                              </button>
                            );
                        })}
                        {(distinctValuesByColumn[column.key] ?? []).filter(
                          (value) =>
                            (column.key === "phaseType"
                              ? formatPhaseTypeLabel(value)
                              : value
                            )
                              .toLocaleLowerCase("de-DE")
                              .includes(
                                filterValueSearch
                                  .trim()
                                  .toLocaleLowerCase("de-DE")
                              )
                        ).length === 0 ? (
                          <div className="header-filter-empty">Keine passenden Werte gefunden.</div>
                        ) : null}
                      </div>
                      <div className="header-filter-footer">
                        {filterDraftValues.length === 0 ? (
                          <span className="header-filter-warning">Mindestens einen Wert auswählen.</span>
                        ) : (
                          <span />
                        )}
                        <div className="header-filter-footer-actions">
                          <button type="button" onClick={closeColumnFilterMenu}>Abbrechen</button>
                          <button
                            type="button"
                            className="primary"
                            onClick={() => applyColumnFilter(column.key)}
                            disabled={filterDraftValues.length === 0}
                          >
                            {filterDraftValues.length === (distinctValuesByColumn[column.key] ?? []).length
                              ? "Alle anzeigen"
                              : "Filter anwenden"}
                          </button>
                        </div>
                      </div>
                    </div>
                  ) : null}
                </th>
              ))}
              <th>Aktionen</th>
            </tr>
          </thead>
          <tbody>
            {visibleRows.map((row) => {
              if (
                row.rowType === "headerComponent" ||
                row.rowType === "groupComponent" ||
                row.rowType === "footerComponent"
              ) {
                const component = row.component!;
                const isMutableComponent =
                  component.role === "group_upstream_protection" ||
                  component.role === "group_residual_current_protection" ||
                  component.role === "auxiliary";
                const protection = component.protectionDevice;
                const protectionSummary = protection
                  ? [
                      protection.type,
                      `${formatValue(
                        protection.ratedCurrentA,
                        "protectionRatedCurrent"
                      )} A`,
                      protection.fuseUtilizationCategory,
                      protection.tripCharacteristic,
                      protection.rcdType
                        ? `Typ ${protection.rcdType}`
                        : undefined,
                      protection.ratedResidualCurrentMa !== undefined
                        ? `${formatValue(
                            protection.ratedResidualCurrentMa,
                            "protectionRatedCurrent"
                          )} mA`
                        : undefined,
                    ]
                      .filter(Boolean)
                      .join(" · ")
                  : null;
                return (
                  <tr
                    key={row.rowKey}
                    className={`structure-component-row ${row.rowType}`}
                  >
                    <td colSpan={visibleColumns.length + 1}>
                      <div className="structure-component-content">
                        <strong>{component.equipmentIdentifier}</strong>
                        <span>{component.name}</span>
                        {protectionSummary ? (
                          <span className="structure-component-protection">
                            {protectionSummary}
                          </span>
                        ) : null}
                        <span className="structure-component-actions">
                          {isMutableComponent ? (
                            <>
                              <button
                                type="button"
                                disabled={isSaving}
                                onClick={() =>
                                  setComponentEditorIntent({
                                    kind: "edit",
                                    component,
                                  })
                                }
                              >
                                Bearbeiten
                              </button>
                              <button
                                type="button"
                                disabled={isSaving}
                                onClick={() =>
                                  void handleDeleteDistributionBoardComponent(
                                    component
                                  )
                                }
                              >
                                Entfernen
                              </button>
                            </>
                          ) : (
                            <span className="structure-component-fixed">
                              Feste Verteilerkomponente
                            </span>
                          )}
                        </span>
                      </div>
                    </td>
                  </tr>
                );
              }
              if (row.rowType === "section") {
                const section = data.sections.find((entry) => entry.id === row.sectionId)!;
                const hasUpstreamProtection = section.components.some(
                  (component) =>
                    component.role === "group_upstream_protection"
                );
                const hasGroupRcd = section.components.some(
                  (component) =>
                    component.role === "group_residual_current_protection"
                );
                return (
                  <tr
                    key={row.rowKey}
                    className={`section-row ${
                      dropIntent?.kind === "new-circuit" && dropIntent.sectionId === section.id && dropIntent.valid
                        ? "drop-target-active"
                        : ""
                    } ${
                      dropIntent?.kind === "new-circuit" && dropIntent.sectionId === section.id && !dropIntent.valid
                        ? "drop-target-invalid"
                        : ""
                    }`}
                    onDragOver={(event) => {
                      if (draggingCircuitCount > 0) {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = "none";
                        setCircuitReorderIntent({
                          kind: "section-end",
                          sectionId: section.id,
                          valid: false,
                        });
                        return;
                      }
                      if (!draggingProjectDeviceId) {
                        return;
                      }
                      const device = projectDevices.find((entry) => entry.id === draggingProjectDeviceId);
                      const valid = Boolean(device && isProjectDevicePlacementValid(device, section));
                      event.preventDefault();
                      event.dataTransfer.dropEffect = valid ? "copy" : "none";
                      setDropIntent({ kind: "new-circuit", sectionId: section.id, valid });
                    }}
                    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) {
                        const device = projectDevices.find((entry) => entry.id === draggingProjectDeviceId);
                        void handleDropWithIntent(event, {
                          kind: "new-circuit",
                          sectionId: section.id,
                          valid: Boolean(device && isProjectDevicePlacementValid(device, section)),
                        });
                        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">
                        <div className="section-title">
                          <strong>
                            {getCircuitSectionLabel(section)}
                          </strong>
                          <span>
                            Gesamtleistung Abschnitt:{" "}
                            {formatValue(
                              section.sectionTotalPower,
                              "rowTotalPower"
                            )}{" "}
                            kW
                          </span>
                        </div>
                        <div className="section-actions">
                          <button
                            type="button"
                            tabIndex={-1}
                            disabled={!section.category || !section.groupNumber}
                            onClick={() =>
                              setCircuitGroupEditorIntent({
                                kind: "edit",
                                section,
                              })
                            }
                          >
                            Gruppe bearbeiten
                          </button>
                          <button
                            type="button"
                            tabIndex={-1}
                            disabled={!canDeleteCircuitGroup(section)}
                            title={
                              canDeleteCircuitGroup(section)
                                ? undefined
                                : "Befüllte Gruppen werden später über einen eigenen Warn- und Löschdialog entfernt."
                            }
                            onClick={() =>
                              void handleDeleteEmptyCircuitGroup(section)
                            }
                          >
                            Gruppe entfernen
                          </button>
                          <button
                            type="button"
                            tabIndex={-1}
                            disabled={
                              hasUpstreamProtection ||
                              !section.category ||
                              !section.groupNumber
                            }
                            onClick={() =>
                              setComponentEditorIntent({
                                kind: "create",
                                role: "group_upstream_protection",
                                sectionId: section.id,
                                initialEquipmentIdentifier:
                                  getSuggestedGroupComponentIdentifier(
                                    section,
                                    "group_upstream_protection"
                                  ),
                              })
                            }
                          >
                            Vorsicherung hinzufügen
                          </button>
                          <button
                            type="button"
                            tabIndex={-1}
                            disabled={
                              hasGroupRcd ||
                              !section.category ||
                              !section.groupNumber
                            }
                            onClick={() =>
                              setComponentEditorIntent({
                                kind: "create",
                                role: "group_residual_current_protection",
                                sectionId: section.id,
                                initialEquipmentIdentifier:
                                  getSuggestedGroupComponentIdentifier(
                                    section,
                                    "group_residual_current_protection"
                                  ),
                              })
                            }
                          >
                            Gruppen-FI hinzufügen
                          </button>
                          <button type="button" tabIndex={-1} onClick={() => void handleAddReserveCircuit(section.id)}>
                            Stromkreis hinzufügen
                          </button>
                          <button
                            type="button"
                            tabIndex={-1}
                            disabled={hasActiveSortOrFilter}
                            onClick={() => void handleRenumberSection(section.id)}
                            title={hasActiveSortOrFilter ? "Vor der Neunummerierung Sortierung und Filter zurücksetzen." : undefined}
                          >
                            Bereich neu nummerieren
                          </button>
                        </div>
                      </div>
                      {dropIntent?.kind === "new-circuit" && dropIntent.sectionId === section.id ? (
                        <span className="drop-hint">
                          {dropIntent.valid ? "Hier ablegen, um einen neuen Stromkreis zu erstellen" : "Das Gerät passt nicht in diesen Bereich"}
                        </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 && dropIntent.valid) ||
                      (deviceMoveIntent?.kind === "move-to-new-circuit" &&
                        deviceMoveIntent.sectionId === row.sectionId &&
                        !deviceMoveIntent.requiresConfirmation) ||
                      (circuitReorderIntent?.kind === "section-end" && circuitReorderIntent.sectionId === row.sectionId))
                      ? "drop-target-active"
                      : ""
                  } ${
                    row.rowType === "placeholder" &&
                    deviceMoveIntent?.kind === "move-to-new-circuit" &&
                    deviceMoveIntent.sectionId === row.sectionId &&
                    deviceMoveIntent.requiresConfirmation
                      ? "drop-target-confirm"
                      : ""
                  } ${
                    row.circuit &&
                    deviceMoveIntent?.kind === "move-to-circuit" &&
                    deviceMoveIntent.circuitId === row.circuit.id &&
                    deviceMoveIntent.requiresConfirmation
                      ? "drop-target-confirm"
                      : ""
                  } ${
                    row.rowType === "placeholder" &&
                    dropIntent?.kind === "new-circuit" &&
                    dropIntent.sectionId === row.sectionId &&
                    !dropIntent.valid
                      ? "drop-target-invalid"
                      : ""
                  } ${
                    row.rowType === "placeholder" &&
                    circuitReorderIntent?.kind === "section-end" &&
                    circuitReorderIntent.sectionId === row.sectionId &&
                    !circuitReorderIntent.valid
                      ? "drop-target-invalid"
                      : ""
                  } ${
                    row.circuit &&
                    dropIntent?.kind === "add-to-circuit" &&
                    dropIntent.circuitId === row.circuit.id &&
                    dropIntent.valid
                      ? "drop-target-active"
                      : ""
                  } ${
                    row.circuit &&
                    dropIntent?.kind === "add-to-circuit" &&
                    dropIntent.circuitId === row.circuit.id &&
                    !dropIntent.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) {
                      const device = projectDevices.find((entry) => entry.id === draggingProjectDeviceId);
                      const section = data.sections.find((entry) => entry.id === row.sectionId);
                      const valid = Boolean(device && section && isProjectDevicePlacementValid(device, section));
                      if (row.rowType === "placeholder") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = valid ? "copy" : "none";
                        setDropIntent({ kind: "new-circuit", sectionId: row.sectionId, valid });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = valid ? "copy" : "none";
                        setDropIntent({
                          kind: "add-to-circuit",
                          circuitId: row.circuit.id,
                          sectionId: row.sectionId,
                          valid,
                        });
                      }
                      return;
                    }
                    if (draggingDeviceCount > 0) {
                      const requiresConfirmation = requiresCrossSectionMoveConfirmation(
                        getDeviceRowSourceSectionIds(activeDraggedDeviceRowIds),
                        row.sectionId
                      );
                      if (row.rowType === "placeholder") {
                        event.preventDefault();
                        event.dataTransfer.dropEffect = "move";
                        setDeviceMoveIntent({
                          kind: "move-to-new-circuit",
                          sectionId: row.sectionId,
                          requiresConfirmation,
                        });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        const sourceCircuitIds = activeDraggedDeviceRowIds
                          .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,
                            requiresConfirmation,
                          });
                        }
                      }
                    }
                  }}
                  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) {
                      const device = projectDevices.find((entry) => entry.id === draggingProjectDeviceId);
                      const section = data.sections.find((entry) => entry.id === row.sectionId);
                      const valid = Boolean(device && section && isProjectDevicePlacementValid(device, section));
                      if (row.rowType === "placeholder") {
                        void handleDropWithIntent(event, { kind: "new-circuit", sectionId: row.sectionId, valid });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        void handleDropWithIntent(event, {
                          kind: "add-to-circuit",
                          circuitId: row.circuit.id,
                          sectionId: row.sectionId,
                          valid,
                        });
                      }
                      return;
                    }
                    if (draggingDeviceCount > 0) {
                      const requiresConfirmation = requiresCrossSectionMoveConfirmation(
                        getDeviceRowSourceSectionIds(activeDraggedDeviceRowIds),
                        row.sectionId
                      );
                      if (row.rowType === "placeholder") {
                        void handleDeviceRowDropWithIntent(event, {
                          kind: "move-to-new-circuit",
                          sectionId: row.sectionId,
                          requiresConfirmation,
                        });
                        return;
                      }
                      if (row.circuit && row.rowType !== "deviceRow") {
                        void handleDeviceRowDropWithIntent(event, {
                          kind: "move-to-circuit",
                          circuitId: row.circuit.id,
                          sectionId: row.sectionId,
                          requiresConfirmation,
                        });
                      }
                    }
                  }}
                >
                  {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;
                    const hasDraftIdentifierConflict = Boolean(
                      isEditing &&
                        column.key === "equipmentIdentifier" &&
                        row.circuit &&
                        hasEquipmentIdentifierConflict(allCircuits, row.circuit.id, editingCell?.draft ?? "")
                    );
                    const hasIdentifierConflict = Boolean(
                      column.key === "equipmentIdentifier" &&
                        row.circuit &&
                        (duplicateEquipmentIdentifierCircuitIds.has(row.circuit.id) || hasDraftIdentifierConflict)
                    );
                    return (
                      <td
                        key={column.key}
                        className={`${column.numeric ? "num" : ""} ${cell.editable ? "cell-editable" : ""} ${isSelected ? "cell-selected" : ""} ${hasIdentifierConflict ? "cell-invalid" : ""} ${
                          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 (isGridEditorControlTarget(event.target)) {
                            return;
                          }
                          if (cell.editable) {
                            handleRowSelectionClick(row, column.key, {
                              ctrlKey: event.ctrlKey,
                              metaKey: event.metaKey,
                              shiftKey: event.shiftKey,
                            });
                          }
                        }}
                        onDoubleClick={(event) => {
                          if (isGridEditorControlTarget(event.target)) {
                            return;
                          }
                          if (cell.editable) {
                            startEdit({ rowKey: row.rowKey, cellKey: column.key }, "selectExisting");
                          }
                        }}
                      >
                        {isEditing && column.key === "phaseType" ? (
                          <select
                            ref={(element) => {
                              inputRef.current = element;
                            }}
                            value={editingCell.draft}
                            aria-label="Phasenart auswählen"
                            onChange={(event) =>
                              void commitEdit("stay", event.target.value)
                            }
                            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);
                                  focusGridWithoutScroll();
                                }
                              });
                            }}
                          >
                            <option value="single_phase">1-phasig</option>
                            <option value="three_phase">3-phasig</option>
                          </select>
                        ) : isEditing ? (
                          <input
                            ref={(element) => {
                              inputRef.current = element;
                            }}
                            value={editingCell.draft}
                            aria-invalid={hasDraftIdentifierConflict}
                            title={hasDraftIdentifierConflict ? "Dieses Betriebsmittelkennzeichen ist bereits vorhanden." : undefined}
                            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);
                                  focusGridWithoutScroll();
                                }
                              });
                            }}
                          />
                        ) : (
                          formatValue(cell.value, column.key)
                        )}
                      </td>
                    );
                  })}
                  <td
                    className={`action-cell ${
                      (dropIntent?.kind === "add-to-circuit" &&
                        dropIntent.circuitId === row.circuit?.id &&
                        dropIntent.valid) ||
                      (deviceMoveIntent?.kind === "move-to-circuit" &&
                        deviceMoveIntent.circuitId === row.circuit?.id &&
                        !deviceMoveIntent.requiresConfirmation)
                        ? "drop-target-active"
                        : ""
                    } ${
                      dropIntent?.kind === "add-to-circuit" &&
                      dropIntent.circuitId === row.circuit?.id &&
                      !dropIntent.valid
                        ? "drop-target-invalid"
                        : ""
                    } ${
                      deviceMoveIntent?.kind === "move-to-circuit" &&
                      deviceMoveIntent.circuitId === row.circuit?.id &&
                      deviceMoveIntent.requiresConfirmation
                        ? "drop-target-confirm"
                        : ""
                    }`}
                  >
                    {row.circuit && row.rowType !== "deviceRow" ? (
                      <>
                        <button
                          type="button"
                          tabIndex={-1}
                          onClick={() => void handleAddManualDevice(row.circuit!, row.sectionId)}
                        >
                          Manuelles Gerät hinzufügen
                        </button>
                        <button
                          type="button"
                          tabIndex={-1}
                          onClick={() => void handleDeleteCircuit(row.circuit!.id)}
                        >
                          Stromkreis löschen
                        </button>
                      </>
                    ) : null}
                    {row.device ? (
                      <button type="button" tabIndex={-1} onClick={() => void handleDeleteDevice(row.device!.id)}>
                        Gerät löschen
                      </button>
                    ) : null}
                    {dropIntent?.kind === "new-circuit" && row.rowType === "placeholder" && dropIntent.sectionId === row.sectionId ? (
                      <span className="drop-hint">
                        {dropIntent.valid ? "Neuer Stromkreis in diesem Bereich" : "Das Gerät passt nicht in diesen Bereich"}
                      </span>
                    ) : null}
                    {dropIntent?.kind === "add-to-circuit" && dropIntent.circuitId === row.circuit?.id ? (
                      <span className="drop-hint">
                        {dropIntent.valid ? "Zu diesem Stromkreis hinzufügen" : "Das Gerät passt nicht in den Bereich dieses Stromkreises"}
                      </span>
                    ) : null}
                    {deviceMoveIntent?.kind === "move-to-new-circuit" &&
                    row.rowType === "placeholder" &&
                    deviceMoveIntent.sectionId === row.sectionId ? (
                      <span className="drop-hint">
                        {`${draggingDeviceCount || 1} Gerätezeile(n) in einen neuen Stromkreis verschieben${deviceMoveIntent.requiresConfirmation ? " (Bestätigung erforderlich)" : ""}`}
                      </span>
                    ) : null}
                    {deviceMoveIntent?.kind === "move-to-circuit" && deviceMoveIntent.circuitId === row.circuit?.id ? (
                      <span className="drop-hint">
                        {`${draggingDeviceCount || 1} Gerätezeile(n) in diesen Stromkreis verschieben${deviceMoveIntent.requiresConfirmation ? " (Bestätigung erforderlich)" : ""}`}
                      </span>
                    ) : null}
                    {circuitReorderIntent?.kind === "section-end" &&
                    row.rowType === "placeholder" &&
                    circuitReorderIntent.sectionId === row.sectionId ? (
                      <span className="drop-hint">
                        {circuitReorderIntent.valid
                          ? `${draggingCircuitCount || 1} Stromkreis(e) ans Bereichsende verschieben`
                          : "Bereichsübergreifendes Verschieben nicht zulässig"}
                      </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"
                            ? `${draggingCircuitCount || 1} Stromkreis(e) vor diesen Stromkreis verschieben`
                            : `${draggingCircuitCount || 1} Stromkreis(e) hinter diesen Stromkreis verschieben`
                          : "Bereichsübergreifendes Verschieben nicht zulässig"}
                      </span>
                    ) : null}
                  </td>
                </tr>
              );
            })}
          </tbody>
          </table>
        </div>
      </div>
      <p className="todo-hint">
        Strg+Plus fügt unterhalb des ausgewählten Stromkreises oder Geräts ein. Mit „Rückgängig“ lässt sich das Einfügen zurücknehmen.
      </p>
    </div>
  );
}
