import { useState } from 'react'
import Icon from './Icon'
import ContextMenu from './ContextMenu'
import { BarCombo, BarButton } from './BarControls'
import { openGeschossSettings, openGeschossDialog, createSchnitt } from '../lib/rhinoBridge'

function GeschossBadge({ name }) {
  return <span className="chip chip-info">{name}</span>
}

function ZeichnungsebeneRow({
  z, active, mode, onClick, onContextMenu,
  onToggleVisible, onToggleLock, onToggleClipping, onDelete,
  onDragStart, onDragOver, onDragLeave, onDrop, onDragEnd,
  isDragging, dropPos,
}) {
  const isGeschoss = !!z.isGeschoss
  const isSchnitt  = z.type === 'schnitt'
  // Schnitt vs Ansicht: cutAtLine!=false = Schnitt (mit Front-Cut), sonst Ansicht
  const schnittIcon = (z.cutAtLine === false) ? 'visibility' : 'content_cut'
  const schnittLabel = (z.cutAtLine === false) ? 'Ansicht' : 'Schnitt'
  // Eye-Logik: die aktive Z ist IMMER sichtbar (Backend forciert das), also
  // zeigen wir ihr Auge immer als "an" — ohne Ruecksicht aufs visible-Flag.
  // Nicht-aktive: in 'all_force' ist visible-Flag ueberschrieben (alle an),
  // in 'active' ueberschrieben (alle aus) — Auge dimmt. Sonst (Ausgewaehlte/
  // grey) reflektiert es das Flag direkt.
  let eyeIcon, eyeOn, eyeOpacity, eyeTitle
  if (active) {
    eyeIcon    = 'visibility'
    eyeOn      = true
    eyeOpacity = 1
    eyeTitle   = z.visible !== false
      ? 'Sichtbar (aktive Zeichnungsebene)'
      : 'Normalerweise ausgeblendet — wird gezeigt weil aktiv'
  } else if (mode === 'all_force') {
    eyeIcon    = 'visibility'
    eyeOn      = true
    eyeOpacity = 0.35
    eyeTitle   = 'Im „Alle anzeigen"-Mode immer sichtbar — Klick wechselt in „Ausgewählte"'
  } else if (mode === 'active') {
    eyeIcon    = z.visible !== false ? 'visibility' : 'visibility_off'
    eyeOn      = false
    eyeOpacity = 0.35
    eyeTitle   = 'Im „Nur aktive"-Mode ausgeblendet — Klick wechselt in „Ausgewählte"'
  } else {
    eyeIcon    = z.visible !== false ? 'visibility' : 'visibility_off'
    eyeOn      = z.visible !== false
    eyeOpacity = 1
    eyeTitle   = z.visible !== false ? 'Ausblenden' : 'Einblenden'
  }
  return (
    <div
      draggable
      onDragStart={(ev) => onDragStart && onDragStart(ev, z.id)}
      onDragOver={(ev) => onDragOver && onDragOver(ev, z.id)}
      onDragLeave={(ev) => onDragLeave && onDragLeave(ev, z.id)}
      onDrop={(ev) => onDrop && onDrop(ev, z.id)}
      onDragEnd={(ev) => onDragEnd && onDragEnd(ev)}
      onClick={onClick}
      onContextMenu={onContextMenu}
      style={{
        display: 'flex', alignItems: 'center', gap: 4,
        padding: '1px 12px 1px 0',
        margin: 0,
        background: active ? 'var(--active-dim)' : 'var(--bg-item)',
        borderRadius: active ? 999 : 0,
        // Drop-Indikator: 2px accent-Linie an Top oder Bottom — zeigt
        // wo der gedragte Eintrag nach Loslassen landet. Sonst normale
        // unten-Trennlinie. isDragging: leichtes Faden des gehobenen
        // Eintrags fuer visuelles Feedback.
        borderTop: dropPos === 'top'
          ? '2px solid var(--accent)' : '0 solid transparent',
        borderBottom: dropPos === 'bottom'
          ? '2px solid var(--accent)'
          : (active ? '1px solid transparent' : '1px solid var(--border-light)'),
        opacity: isDragging ? 0.4 : 1,
        cursor: 'pointer',
        userSelect: 'none',
        minHeight: 24,
      }}
    >
      {/* Spacer-Slot — spiegelt den Chevron-Slot bei Ebenen-Rows wider
          damit die Eye-Icons beider Panels untereinander stehen. */}
      <span style={{ width: 12, flexShrink: 0 }} />
      <button
        className={`btn-icon-sm ${eyeOn ? 'is-on' : ''}`}
        onClick={(ev) => { ev.stopPropagation(); onToggleVisible() }}
        title={eyeTitle}
        style={{ opacity: eyeOpacity, width: 16, height: 16 }}
      ><Icon name={eyeIcon} size={12} /></button>

      <span style={{
        fontWeight: 500,
        fontSize: 11,
        color: active ? 'var(--active-light)' : 'var(--text-label)',
        flex: 1, minWidth: 0,
        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        lineHeight: 1.2,
      }}>{z.name}</span>

      {isGeschoss && (
        <span style={{ fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>
          +{(z.okff ?? 0).toFixed(2)}
        </span>
      )}

      {isGeschoss && <GeschossBadge name={z.name} />}

      {isSchnitt && (
        <span title={schnittLabel}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 3,
                fontSize: 10, color: 'var(--text-muted)',
                fontFamily: 'var(--font-mono)',
              }}>
          <Icon name={schnittIcon} size={11}
                style={{ color: active ? 'var(--accent)' : 'var(--text-muted)' }} />
        </span>
      )}

      {isGeschoss ? (
        <button
          className={`btn-icon-xs ${z.hasClipping ? 'is-on' : ''}`}
          onClick={(ev) => { ev.stopPropagation(); onToggleClipping() }}
          title={z.hasClipping
            ? 'Clipping Plane ausschalten'
            : 'Clipping Plane einschalten (Schnitt auf Schnitthöhe)'}
          style={{ color: z.hasClipping ? 'var(--accent)' : undefined, width: 14, height: 14 }}
        ><Icon name="content_cut" size={11} /></button>
      ) : (
        <span style={{ width: 14, flexShrink: 0 }} />
      )}

      <button
        className="btn-icon-xs"
        onClick={(ev) => { ev.stopPropagation(); onToggleLock() }}
        title={z.locked ? 'Entsperren' : 'Sperren'}
        style={{ color: z.locked ? 'var(--warn)' : undefined, width: 14, height: 14 }}
      ><Icon name={z.locked ? 'lock' : 'lock_open'} size={11} /></button>

      <button
        className="btn-icon-xs"
        onClick={(ev) => { ev.stopPropagation(); onDelete() }}
        title="Löschen"
        style={{ width: 14, height: 14 }}
      ><Icon name="close" size={11} /></button>
    </div>
  )
}

const MODES = [
  { value: 'all_force',   label: 'Alle anzeigen' },
  { value: 'all',         label: 'Ausgewählte' },
  { value: 'active',      label: 'Nur aktive' },
  { value: 'grey',        label: 'Andere grau' },
  { value: 'grey_locked', label: 'Andere grau & gesperrt' },
]

export default function GeschossManager({
  zeichnungsebenen, activeId, onActiveChange, onChange, recalcOkff,
  mode, onModeChange,
  projectSettings,
}) {
  const [ctxMenu, setCtxMenu] = useState(null)  // { x, y, id }
  const [addMenu, setAddMenu] = useState(null)  // { x, y } — Picker beim +
  const [geschossDialog, setGeschossDialog] = useState(null)  // { x, y, pos:'above'|'below', name, hoehe, schnitthoehe, anchorId }
  // Drag-State fuer Reorder
  const [dragId,      setDragId]      = useState(null)
  const [dropOverId,  setDropOverId]  = useState(null)
  const [dropPos,     setDropPos]     = useState(null)  // 'top' | 'bottom'

  const handleDragStart = (ev, id) => {
    setDragId(id)
    try {
      ev.dataTransfer.effectAllowed = 'move'
      ev.dataTransfer.setData('text/plain', id)
    } catch (e) {}
  }
  const handleDragOver = (ev, targetId) => {
    if (!dragId || dragId === targetId) return
    ev.preventDefault()
    try { ev.dataTransfer.dropEffect = 'move' } catch (e) {}
    const rect = ev.currentTarget.getBoundingClientRect()
    const pos = (ev.clientY < rect.top + rect.height / 2) ? 'top' : 'bottom'
    if (targetId !== dropOverId) setDropOverId(targetId)
    if (pos !== dropPos) setDropPos(pos)
  }
  const handleDragLeave = (ev, targetId) => {
    // Wenn der Cursor die ganze Row verlaesst (nicht in ein Child), reset.
    // Heuristik: relatedTarget liegt nicht innerhalb der Row.
    if (!ev.currentTarget.contains(ev.relatedTarget)) {
      if (dropOverId === targetId) {
        setDropOverId(null); setDropPos(null)
      }
    }
  }
  const handleDrop = (ev, targetId) => {
    ev.preventDefault()
    const srcId = dragId
    setDragId(null); setDropOverId(null); setDropPos(null)
    if (!srcId || srcId === targetId) return
    // Array-Reihenfolge: bottom-up (recalcOkff stacks von index 0 hoch).
    // Display ist umgekehrt (top = letztes Array-Element). Klick auf
    // 'top' eines Display-Eintrags Z heisst: oberhalb Z, d.h. nach Z
    // im Array (höherer Index). 'bottom' = unterhalb Z in Display =
    // vor Z im Array.
    const arr = [...zeichnungsebenen]
    const srcIdx = arr.findIndex(z => z.id === srcId)
    if (srcIdx < 0) return
    const [moved] = arr.splice(srcIdx, 1)
    const tgtIdx = arr.findIndex(z => z.id === targetId)
    if (tgtIdx < 0) return
    const insertIdx = (dropPos === 'top') ? tgtIdx + 1 : tgtIdx
    arr.splice(insertIdx, 0, moved)
    onChange(arr)  // ZeichnungsebenenApp ruft recalcOkff → OKFFs stimmen wieder
  }
  const handleDragEnd = () => {
    setDragId(null); setDropOverId(null); setDropPos(null)
  }

  const sorted = [...zeichnungsebenen].reverse()

  const addZeichnung = () => {
    const nonGeschossCount = zeichnungsebenen.filter(
      z => !z.isGeschoss && z.type !== "schnitt").length
    const newZ = {
      id: `z_${Date.now()}`,
      name: `Zeichnung ${nonGeschossCount + 1}`,
      isGeschoss: false,
      visible: true,
    }
    onChange([...zeichnungsebenen, newZ])
  }

  // Vorgeschlagener Geschossname relativ zum Anker. Logik:
  //   EG + above → 1OG, EG + below → UG
  //   <N>OG + above → <N+1>OG, <N>OG + below → wenn N=1 dann EG, sonst <N-1>OG
  //   <N>UG + above → wenn N=1 dann EG, sonst <N-1>UG; <N>UG + below → <N+1>UG
  //   sonst: 'Neu'
  const suggestGeschossName = (anchor, pos) => {
    const nm = ((anchor?.name) || '').trim().toUpperCase()
    const ogMatch = nm.match(/^(\d*)OG$/)
    const ugMatch = nm.match(/^(\d*)UG$/)
    if (nm === 'EG') return pos === 'above' ? '1OG' : 'UG'
    if (ogMatch) {
      const n = parseInt(ogMatch[1] || '1', 10)
      if (pos === 'above') return `${n + 1}OG`
      return n <= 1 ? 'EG' : `${n - 1}OG`
    }
    if (ugMatch) {
      const n = parseInt(ugMatch[1] || '1', 10)
      if (pos === 'below') return `${n + 1}UG`
      return n <= 1 ? 'EG' : `${n - 1}UG`
    }
    return 'Neu'
  }

  // Default-Hoehe: Hierarchie
  //   1. Project-Settings (zentrale Voreinstellung — explizit gesetzt)
  //   2. Aktives Geschoss (wahrscheinlich gleich vom User gewuenscht)
  //   3. Erstes Geschoss in der Liste
  //   4. Hartcodiert 3.0
  // Project-Settings ueberschreibt alle anderen wenn vorhanden, damit
  // der User eine zentrale Vorgabe haben kann.
  const projDefaults = projectSettings?.defaults || {}
  const defaultGeschossHoehe = () => {
    if (projDefaults.geschossHoehe != null) return projDefaults.geschossHoehe
    const act = zeichnungsebenen.find(z => z.id === activeId)
    if (act?.isGeschoss && act.hoehe != null) return act.hoehe
    const first = zeichnungsebenen.find(z => z.isGeschoss && z.hoehe != null)
    return first?.hoehe ?? 3.0
  }
  const defaultSchnitthoehe = () => {
    if (projDefaults.schnitthoehe != null) return projDefaults.schnitthoehe
    const act = zeichnungsebenen.find(z => z.id === activeId)
    if (act?.isGeschoss && act.schnitthoehe != null) return act.schnitthoehe
    const first = zeichnungsebenen.find(z => z.isGeschoss && z.schnitthoehe != null)
    return first?.schnitthoehe ?? 1.0
  }

  const openGeschossPrompt = (ev) => {
    // Anker = aktives Geschoss (oder erstes Geschoss in der Liste). Falls
    // gar keins da: einfach default-Werte ohne Anker.
    const anchor = zeichnungsebenen.find(z => z.id === activeId && z.isGeschoss)
                || zeichnungsebenen.find(z => z.isGeschoss)
                || null
    const pos = 'above'
    const rect = ev?.currentTarget?.getBoundingClientRect()
    setGeschossDialog({
      x: rect ? rect.right - 240 : 200,
      y: rect ? rect.bottom + 4 : 100,
      pos,
      name: suggestGeschossName(anchor, pos),
      hoehe: defaultGeschossHoehe().toFixed(2),
      schnitthoehe: defaultSchnitthoehe().toFixed(2),
      anchorId: anchor?.id || null,
    })
  }

  const confirmGeschoss = () => {
    if (!geschossDialog) return
    const { pos, name, hoehe, schnitthoehe, anchorId } = geschossDialog
    const h = parseFloat(String(hoehe).replace(',', '.'))
    const sh = parseFloat(String(schnitthoehe).replace(',', '.'))
    const newZ = {
      id: `z_${Date.now()}`,
      name: (name || 'Neu').trim(),
      isGeschoss: true,
      hoehe: isFinite(h) && h > 0 ? h : 3.0,
      schnitthoehe: isFinite(sh) && sh > 0 ? sh : 1.0,
      visible: true,
    }
    // Insertion-Index: anchor finden, dann +1 (above) oder vorne ein (below).
    // Array-Reihenfolge = bottom-up (recalcOkff stacks von index 0 aufwaerts),
    // also "above" = nach anchor, "below" = vor anchor.
    let nextList
    if (anchorId) {
      const idx = zeichnungsebenen.findIndex(z => z.id === anchorId)
      if (idx >= 0) {
        const insertAt = pos === 'above' ? idx + 1 : idx
        nextList = [
          ...zeichnungsebenen.slice(0, insertAt),
          newZ,
          ...zeichnungsebenen.slice(insertAt),
        ]
      } else {
        nextList = [...zeichnungsebenen, newZ]
      }
    } else {
      nextList = [...zeichnungsebenen, newZ]
    }
    onChange(nextList)
    setGeschossDialog(null)
  }

  const updateGeschossDialog = (patch) => {
    if (!geschossDialog) return
    const next = { ...geschossDialog, ...patch }
    // Wenn pos ODER anchorId sich aendert: Name + Hoehen neu vorschlagen,
    // aber nur wenn der User die jeweiligen Felder nicht schon manuell
    // ueberschrieben hat (heuristisch: aktueller Wert === alter Vorschlag).
    const posChanged    = patch.pos      && patch.pos      !== geschossDialog.pos
    const anchorChanged = patch.anchorId && patch.anchorId !== geschossDialog.anchorId
    if (posChanged || anchorChanged) {
      const oldAnchor = zeichnungsebenen.find(z => z.id === geschossDialog.anchorId)
      const newAnchor = zeichnungsebenen.find(z => z.id === (patch.anchorId || geschossDialog.anchorId))
      const oldSuggestedName = suggestGeschossName(oldAnchor, geschossDialog.pos)
      if (geschossDialog.name === oldSuggestedName) {
        next.name = suggestGeschossName(newAnchor, next.pos)
      }
      // Bei Anchor-Wechsel: Hoehe/Schnitthoehe auch nachziehen wenn unveraendert
      if (anchorChanged && newAnchor?.isGeschoss) {
        const oldHoeheStr = (oldAnchor?.hoehe ?? 3.0).toFixed(2)
        const oldShStr    = (oldAnchor?.schnitthoehe ?? 1.0).toFixed(2)
        if (geschossDialog.hoehe === oldHoeheStr && newAnchor.hoehe != null) {
          next.hoehe = newAnchor.hoehe.toFixed(2)
        }
        if (geschossDialog.schnitthoehe === oldShStr && newAnchor.schnitthoehe != null) {
          next.schnitthoehe = newAnchor.schnitthoehe.toFixed(2)
        }
      }
    }
    setGeschossDialog(next)
  }

  const addSchnitt = () => createSchnitt()  // triggert interaktiven Pick + Auto-Activate

  const openAddMenu = (ev) => {
    if (!ev) { addZeichnung(); return }  // Fallback ohne Event-Position
    const rect = ev.currentTarget.getBoundingClientRect()
    setAddMenu({ x: rect.right - 180, y: rect.bottom + 4 })
  }

  // ContextMenu Items — onClick kriegt KEIN Event. Daher fuer Geschoss
  // einen kleinen Trick: ContextMenu schliesst, dann oeffnen wir den
  // Dialog mit einer kuenstlichen Position (rechts vom Panel).
  const addMenuItems = [
    { label: 'Geschoss',          icon: 'layers',
      onClick: () => openGeschossPrompt({
        currentTarget: { getBoundingClientRect: () =>
          ({ right: addMenu?.x + 180, bottom: addMenu?.y + 20 }) } }) },
    { label: 'Schnitt / Ansicht', icon: 'content_cut',    onClick: addSchnitt },
    { divider: true },
    { label: 'Zeichnung',         icon: 'edit_note',      onClick: addZeichnung },
  ]

  const toggleVisible = (id) => {
    onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, visible: !(z.visible !== false) } : z))
    // In "active" / "all_force" greift visible-Flag nicht — wer aufs Auge
    // klickt will offensichtlich Sichtbarkeit kontrollieren, also direkt
    // in den "Ausgewählte"-Mode wechseln damit die Aktion wirkt.
    if (mode === 'active' || mode === 'all_force') onModeChange('all')
  }

  const toggleLock = (id) => {
    onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, locked: !z.locked } : z))
  }

  const toggleClipping = (id) => {
    onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, hasClipping: !z.hasClipping } : z))
    // Backend rendert die Clipping-Plane nur fuer das AKTIVE Geschoss.
    // Klick auf cut-Icon einer nicht-aktiven Zeile → mit-aktivieren,
    // sonst kriegt der User keine sichtbare Rueckmeldung.
    if (id !== activeId) onActiveChange(id)
  }

  const duplicate = (id) => {
    const src = zeichnungsebenen.find(z => z.id === id)
    if (!src) return
    const clone = {
      ...src,
      id: `z_${Date.now()}`,
      name: `${src.name} Kopie`,
    }
    // Direkt nach dem Original einfuegen
    const idx = zeichnungsebenen.findIndex(z => z.id === id)
    const next = [...zeichnungsebenen]
    next.splice(idx + 1, 0, clone)
    onChange(next)
  }

  const remove = (id) => {
    if (zeichnungsebenen.length <= 1) return
    const target = zeichnungsebenen.find(z => z.id === id)
    if (!target) return
    if (!window.confirm(`"${target.name}" wirklich löschen?`)) return
    onChange(zeichnungsebenen.filter(z => z.id !== id))
    if (activeId === id) {
      const next = zeichnungsebenen.find(z => z.id !== id)
      if (next) onActiveChange(next.id)
    }
  }

  const openContextMenu = (ev, id) => {
    ev.preventDefault(); ev.stopPropagation()
    setCtxMenu({ x: ev.clientX, y: ev.clientY, id })
  }

  const ctxItems = (id) => {
    const z = zeichnungsebenen.find(x => x.id === id)
    if (!z) return []
    return [
      { label: 'Einstellungen…',  icon: 'settings',     onClick: () => openGeschossSettings(z) },
      { divider: true },
      { label: 'Duplizieren',     icon: 'content_copy', onClick: () => duplicate(id) },
      { divider: true },
      { label: 'Löschen',         icon: 'delete',       danger: true,
        disabled: zeichnungsebenen.length <= 1,
        onClick: () => remove(id) },
    ]
  }

  return (
    <>
      <div style={{
        display: 'flex', flexDirection: 'column', gap: 4,
        padding: '6px 14px',
        background: 'var(--bg-section)',
        borderBottom: '1px solid var(--border-light)',
      }}>
        <span className="label-xs">Sichtbarkeit</span>
        <div style={{ display: 'flex', width: '100%' }}>
          <BarCombo
            stretch
            icon="visibility"
            value={mode}
            onChange={onModeChange}
            title="Sichtbarkeits-Modus"
            onGear={() => openGeschossDialog(zeichnungsebenen)}
            gearIcon="settings"
            gearTitle="Einstellungen"
            onSecond={openAddMenu}
            secondIcon="add"
            secondTitle="Hinzufuegen: Geschoss / Schnitt / Zeichnung"
          >
            {MODES.map(m => (
              <option key={m.value} value={m.value}>{m.label}</option>
            ))}
          </BarCombo>
        </div>
      </div>

      {/* Master-Row analog EbenenManager. Padding + Icon-Sizes identisch
          damit beide Panels visuell kohaerent sind. Erste 12px-Spanne
          spiegelt den Chevron-Slot der Ebenen-Daten-Rows wider. */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 4,
        padding: '2px 12px 2px 0',
        background: 'var(--bg-section)',
        borderBottom: '1px solid var(--border-light)',
      }}>
        <span style={{ width: 12, flexShrink: 0 }} />
        <button
          className="btn-icon-xs"
          onClick={() => {
            const anyVisible = zeichnungsebenen.some(z => z.visible !== false)
            onChange(zeichnungsebenen.map(z => ({ ...z, visible: !anyVisible })))
            if (mode === 'active' || mode === 'all_force') onModeChange('all')
          }}
          title={zeichnungsebenen.every(z => z.visible !== false)
            ? 'Alle Zeichnungsebenen ausblenden'
            : 'Alle Zeichnungsebenen einblenden'}
          style={{ width: 16, height: 16,
                    opacity: (mode === 'active' || mode === 'all_force') ? 0.5 : 1 }}
        >
          <Icon
            name={zeichnungsebenen.every(z => z.visible !== false) ? 'visibility' : 'visibility_off'}
            size={11}
          />
        </button>
        <span style={{ flex: 1 }} />
        <button
          className="btn-icon-xs"
          onClick={() => {
            const anyLocked = zeichnungsebenen.some(z => z.locked === true)
            onChange(zeichnungsebenen.map(z => ({ ...z, locked: !anyLocked })))
          }}
          title={zeichnungsebenen.every(z => z.locked === true)
            ? 'Alle Zeichnungsebenen entsperren'
            : 'Alle Zeichnungsebenen sperren'}
          style={{ width: 14, height: 14 }}
        >
          <Icon
            name={zeichnungsebenen.every(z => z.locked === true) ? 'lock' : 'lock_open'}
            size={11}
          />
        </button>
        <div style={{ width: 14 }} />
      </div>

      <div>
        {sorted.map(z => (
          <ZeichnungsebeneRow
            key={z.id}
            z={z}
            active={z.id === activeId}
            mode={mode}
            onClick={() => onActiveChange(z.id)}
            onContextMenu={(ev) => openContextMenu(ev, z.id)}
            onToggleVisible={() => toggleVisible(z.id)}
            onToggleLock={() => toggleLock(z.id)}
            onToggleClipping={() => toggleClipping(z.id)}
            onDelete={() => remove(z.id)}
            isDragging={dragId === z.id}
            dropPos={dropOverId === z.id ? dropPos : null}
            onDragStart={handleDragStart}
            onDragOver={handleDragOver}
            onDragLeave={handleDragLeave}
            onDrop={handleDrop}
            onDragEnd={handleDragEnd}
          />
        ))}
      </div>

      {ctxMenu && (
        <ContextMenu
          x={ctxMenu.x} y={ctxMenu.y}
          items={ctxItems(ctxMenu.id)}
          onClose={() => setCtxMenu(null)}
        />
      )}
      {addMenu && (
        <ContextMenu
          x={addMenu.x} y={addMenu.y}
          items={addMenuItems}
          onClose={() => setAddMenu(null)}
        />
      )}
      {geschossDialog && (() => {
        // Geschoss-Liste (top-down sortiert wie im Panel) fuer das Dropdown
        const geschossOptions = [...zeichnungsebenen]
          .filter(z => z.isGeschoss)
          .reverse()
        return (
        <>
          {/* Backdrop — Klick schliesst */}
          <div onClick={() => setGeschossDialog(null)}
                style={{ position: 'fixed', inset: 0, zIndex: 999,
                          background: 'transparent' }} />
          <div style={{
            position: 'fixed', zIndex: 1000,
            left: Math.max(8, Math.min(geschossDialog.x, window.innerWidth - 260)),
            top:  Math.max(8, Math.min(geschossDialog.y, window.innerHeight - 220)),
            width: 240,
            background: 'var(--bg-dialog)', color: 'var(--text-primary)',
            border: '1px solid var(--border)', borderRadius: 6,
            boxShadow: '0 4px 16px rgba(0,0,0,0.3)',
            padding: 10,
            fontSize: 11, fontFamily: 'var(--font)',
            display: 'flex', flexDirection: 'column', gap: 8,
          }}
          onClick={(ev) => ev.stopPropagation()}>
            <div style={{ fontSize: 10, color: 'var(--text-muted)',
                           fontWeight: 600, letterSpacing: 0.4,
                           textTransform: 'uppercase' }}>
              Neues Geschoss
            </div>

            {/* Position: Anker-Dropdown + Über/Unter-Toggle */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
              <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>
                Position relativ zu
              </span>
              <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
                <select
                  value={geschossDialog.anchorId || ''}
                  onChange={(ev) => updateGeschossDialog({ anchorId: ev.target.value })}
                  disabled={geschossOptions.length === 0}
                  style={{ flex: 1, fontSize: 11, minWidth: 0 }}>
                  {geschossOptions.length === 0 && <option value="">— keins —</option>}
                  {geschossOptions.map(g => (
                    <option key={g.id} value={g.id}>{g.name}</option>
                  ))}
                </select>
                <BarButton icon="arrow_upward"
                  active={geschossDialog.pos === 'above'}
                  onClick={() => updateGeschossDialog({ pos: 'above' })}
                  title="Über dem Anker einfügen" />
                <BarButton icon="arrow_downward"
                  active={geschossDialog.pos === 'below'}
                  onClick={() => updateGeschossDialog({ pos: 'below' })}
                  title="Unter dem Anker einfügen" />
              </div>
            </div>

            {/* Name */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
              <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>Name</span>
              <input type="text" value={geschossDialog.name}
                      onChange={(ev) => updateGeschossDialog({ name: ev.target.value })}
                      autoFocus
                      onKeyDown={(ev) => {
                        if (ev.key === 'Enter') confirmGeschoss()
                        else if (ev.key === 'Escape') setGeschossDialog(null)
                      }}
                      style={{ width: '100%' }} />
            </div>

            {/* Hoehe + Schnitthoehe */}
            <div style={{ display: 'flex', gap: 6 }}>
              <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 3 }}>
                <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>Höhe (m)</span>
                <input type="text" value={geschossDialog.hoehe}
                        onChange={(ev) => updateGeschossDialog({ hoehe: ev.target.value })}
                        onKeyDown={(ev) => {
                          if (ev.key === 'Enter') confirmGeschoss()
                          else if (ev.key === 'Escape') setGeschossDialog(null)
                        }}
                        style={{ width: '100%', textAlign: 'right' }} />
              </div>
              <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 3 }}>
                <span style={{ fontSize: 9, color: 'var(--text-muted)' }}
                       title="Höhe der horizontalen Schnitt-Plane über OKFF">
                  Schnitt (m)
                </span>
                <input type="text" value={geschossDialog.schnitthoehe}
                        onChange={(ev) => updateGeschossDialog({ schnitthoehe: ev.target.value })}
                        onKeyDown={(ev) => {
                          if (ev.key === 'Enter') confirmGeschoss()
                          else if (ev.key === 'Escape') setGeschossDialog(null)
                        }}
                        style={{ width: '100%', textAlign: 'right' }} />
              </div>
            </div>

            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 4, marginTop: 4 }}>
              <button className="btn-text"
                onClick={() => setGeschossDialog(null)}>Abbrechen</button>
              <button className="btn-contained"
                onClick={confirmGeschoss}>Hinzufügen</button>
            </div>
          </div>
        </>
        )
      })()}
    </>
  )
}
