'use client'

import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import dynamic from 'next/dynamic'
import type { GridResult, GridCell, ColumnResult, RowResult, PageZone, PageRegion, RowItem, StructureResult, StructureBox, StructureGraphic } from '@/app/(admin)/ai/ocr-kombi/types'
import { usePixelWordPositions } from './usePixelWordPositions'

const KLAUSUR_API = '/klausur-api'

// Lazy-load Fabric.js canvas editor (SSR-incompatible)
const FabricReconstructionCanvas = dynamic(
  () => import('./FabricReconstructionCanvas').then(m => ({ default: m.FabricReconstructionCanvas })),
  { ssr: false, loading: () => <div className="py-8 text-center text-sm text-gray-400">Editor wird geladen...</div> }
)

type EditorMode = 'simple' | 'editor' | 'overlay'

interface StepReconstructionProps {
  sessionId: string | null
  onNext: () => void
}

interface EditableCell {
  cellId: string
  text: string
  originalText: string
  bboxPct: { x: number; y: number; w: number; h: number }
  colType: string
  rowIndex: number
  colIndex: number
}

type UndoAction = { cellId: string; oldText: string; newText: string }

export function StepReconstruction({ sessionId, onNext }: StepReconstructionProps) {
  const [status, setStatus] = useState<'loading' | 'ready' | 'saving' | 'saved' | 'error'>('loading')
  const [error, setError] = useState('')
  const [cells, setCells] = useState<EditableCell[]>([])
  const [gridCells, setGridCells] = useState<GridCell[]>([])
  const [editorMode, setEditorMode] = useState<EditorMode>('simple')
  const [editedTexts, setEditedTexts] = useState<Map<string, string>>(new Map())
  const [zoom, setZoom] = useState(100)
  const [imageNaturalH, setImageNaturalH] = useState(0)
  const [showEmptyHighlight, setShowEmptyHighlight] = useState(true)

  // Undo/Redo stacks
  const [undoStack, setUndoStack] = useState<UndoAction[]>([])
  const [redoStack, setRedoStack] = useState<UndoAction[]>([])

  const containerRef = useRef<HTMLDivElement>(null)
  const imageRef = useRef<HTMLImageElement>(null)

  // Overlay mode state
  const [isParentWithBoxes, setIsParentWithBoxes] = useState(false)
  const [mergedGridCells, setMergedGridCells] = useState<GridCell[]>([])
  const [parentColumns, setParentColumns] = useState<PageRegion[]>([])
  const [parentRows, setParentRows] = useState<RowItem[]>([])
  const [parentZones, setParentZones] = useState<PageZone[]>([])
  const [imageNaturalSize, setImageNaturalSize] = useState<{ w: number; h: number } | null>(null)
  const [fontScale, setFontScale] = useState(0.7)
  const [globalBold, setGlobalBold] = useState(false)
  const [imageRotation, setImageRotation] = useState<0 | 180>(0)
  const [structureBoxes, setStructureBoxes] = useState<StructureBox[]>([])
  const [structureGraphics, setStructureGraphics] = useState<StructureGraphic[]>([])
  const [showStructure, setShowStructure] = useState(true)
  const reconRef = useRef<HTMLDivElement>(null)
  const [reconWidth, setReconWidth] = useState(0)

  // Pixel-based word positions for overlay mode
  const overlayImageUrl = sessionId
    ? `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/cropped`
    : ''
  const cellWordPositions = usePixelWordPositions(
    overlayImageUrl,
    mergedGridCells,
    editorMode === 'overlay',
    imageRotation,
  )

  // Track reconstruction container width for font size calculation
  useEffect(() => {
    const el = reconRef.current
    if (!el) return
    const obs = new ResizeObserver(entries => {
      for (const entry of entries) setReconWidth(entry.contentRect.width)
    })
    obs.observe(el)
    return () => obs.disconnect()
  }, [editorMode])

  // Load session data on mount
  useEffect(() => {
    if (!sessionId) return
    loadSessionData()
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sessionId])

  // Track image natural dimensions for font scaling and structure layer
  const handleImageLoad = useCallback(() => {
    if (imageRef.current) {
      setImageNaturalH(imageRef.current.naturalHeight)
      if (!imageNaturalSize) {
        setImageNaturalSize({ w: imageRef.current.naturalWidth, h: imageRef.current.naturalHeight })
      }
    }
  }, [imageNaturalSize])

  const loadSessionData = async () => {
    if (!sessionId) return
    setStatus('loading')
    try {
      const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}`)
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      const data = await res.json()

      const wordResult: GridResult | undefined = data.word_result
      if (!wordResult) {
        setError('Keine Worterkennungsdaten gefunden. Bitte zuerst Schritt 5 abschliessen.')
        setStatus('error')
        return
      }

      // Build editable cells from grid cells
      const rawGridCells: GridCell[] = wordResult.cells || []
      setGridCells(rawGridCells)
      const allEditableCells: EditableCell[] = rawGridCells.map(c => ({
        cellId: c.cell_id,
        text: c.text,
        originalText: c.text,
        bboxPct: c.bbox_pct,
        colType: c.col_type,
        rowIndex: c.row_index,
        colIndex: c.col_index,
      }))

      setCells(allEditableCells)
      setEditedTexts(new Map())
      setUndoStack([])
      setRedoStack([])

      // Load structure result (boxes, graphics, colors)
      const structureResult: StructureResult | undefined = data.structure_result
      if (structureResult) {
        setStructureBoxes(structureResult.boxes || [])
        setStructureGraphics(structureResult.graphics || [])
      }

      // Check for parent with boxes (sub-sessions + zones)
      const columnResult: ColumnResult | undefined = data.column_result
      const rowResult: RowResult | undefined = data.row_result
      const subSessions: { id: string; box_index: number }[] = data.sub_sessions || []
      const zones: PageZone[] = columnResult?.zones || []
      const hasBoxes = subSessions.length > 0 && zones.some(z => z.zone_type === 'box')

      setIsParentWithBoxes(hasBoxes)
      if (hasBoxes) setImageRotation(180) // Default: rotate for correct pixel matching

      if (columnResult?.columns) setParentColumns(columnResult.columns)
      if (rowResult?.rows) setParentRows(rowResult.rows)
      if (zones.length > 0) setParentZones(zones)

      // Store image dimensions
      if (wordResult.image_width && wordResult.image_height) {
        setImageNaturalSize({ w: wordResult.image_width, h: wordResult.image_height })
      }

      if (hasBoxes) {
        // Default to overlay mode for parent sessions with boxes
        setEditorMode('overlay')

        // Load sub-sessions and merge cells
        const imgW = wordResult.image_width || 1
        const imgH = wordResult.image_height || 1
        const allMergedCells: GridCell[] = [...rawGridCells]

        for (const sub of subSessions) {
          try {
            const subRes = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sub.id}`)
            if (!subRes.ok) continue
            const subData = await subRes.json()
            const subWordResult: GridResult | undefined = subData.word_result
            if (!subWordResult?.cells) continue

            // Find the box zone for this sub-session
            const boxZone = zones.find(z => z.zone_type === 'box')
            if (!boxZone?.box) continue

            const box = boxZone.box
            // Box coordinates are in pixels, convert to pct
            const boxXPct = (box.x / imgW) * 100
            const boxYPct = (box.y / imgH) * 100
            const boxWPct = (box.width / imgW) * 100
            const boxHPct = (box.height / imgH) * 100

            // Convert sub-session cell coordinates to parent coordinates
            for (const subCell of subWordResult.cells) {
              if (!subCell.bbox_pct) continue
              const parentCellX = boxXPct + (subCell.bbox_pct.x / 100) * boxWPct
              const parentCellY = boxYPct + (subCell.bbox_pct.y / 100) * boxHPct
              const parentCellW = (subCell.bbox_pct.w / 100) * boxWPct
              const parentCellH = (subCell.bbox_pct.h / 100) * boxHPct

              allMergedCells.push({
                ...subCell,
                cell_id: `sub_${sub.id}_${subCell.cell_id}`,
                bbox_pct: {
                  x: parentCellX,
                  y: parentCellY,
                  w: parentCellW,
                  h: parentCellH,
                },
                bbox_px: {
                  x: Math.round(parentCellX / 100 * imgW),
                  y: Math.round(parentCellY / 100 * imgH),
                  w: Math.round(parentCellW / 100 * imgW),
                  h: Math.round(parentCellH / 100 * imgH),
                },
              })
            }
          } catch {
            // Skip failing sub-sessions
          }
        }

        setMergedGridCells(allMergedCells)

        // Also add merged cells as editable cells
        const mergedEditableCells: EditableCell[] = allMergedCells.map(c => ({
          cellId: c.cell_id,
          text: c.text,
          originalText: c.text,
          bboxPct: c.bbox_pct,
          colType: c.col_type,
          rowIndex: c.row_index,
          colIndex: c.col_index,
        }))
        setCells(mergedEditableCells)
      } else {
        setMergedGridCells(rawGridCells)
      }

      setStatus('ready')
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : String(e))
      setStatus('error')
    }
  }

  const handleTextChange = useCallback((cellId: string, newText: string) => {
    setEditedTexts(prev => {
      const oldText = prev.get(cellId)
      const cell = cells.find(c => c.cellId === cellId)
      const prevText = oldText ?? cell?.text ?? ''

      // Push to undo stack
      setUndoStack(stack => [...stack, { cellId, oldText: prevText, newText }])
      setRedoStack([]) // Clear redo on new edit

      const next = new Map(prev)
      next.set(cellId, newText)
      return next
    })
  }, [cells])

  const undo = useCallback(() => {
    setUndoStack(stack => {
      if (stack.length === 0) return stack
      const action = stack[stack.length - 1]
      const newStack = stack.slice(0, -1)

      setRedoStack(rs => [...rs, action])
      setEditedTexts(prev => {
        const next = new Map(prev)
        next.set(action.cellId, action.oldText)
        return next
      })

      return newStack
    })
  }, [])

  const redo = useCallback(() => {
    setRedoStack(stack => {
      if (stack.length === 0) return stack
      const action = stack[stack.length - 1]
      const newStack = stack.slice(0, -1)

      setUndoStack(us => [...us, action])
      setEditedTexts(prev => {
        const next = new Map(prev)
        next.set(action.cellId, action.newText)
        return next
      })

      return newStack
    })
  }, [])

  const resetCell = useCallback((cellId: string) => {
    const cell = cells.find(c => c.cellId === cellId)
    if (!cell) return
    setEditedTexts(prev => {
      const next = new Map(prev)
      next.delete(cellId)
      return next
    })
  }, [cells])

  // Global keyboard shortcuts for undo/redo
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'z') {
        e.preventDefault()
        if (e.shiftKey) {
          redo()
        } else {
          undo()
        }
      }
    }
    document.addEventListener('keydown', handler)
    return () => document.removeEventListener('keydown', handler)
  }, [undo, redo])

  const getDisplayText = useCallback((cell: EditableCell): string => {
    return editedTexts.get(cell.cellId) ?? cell.text
  }, [editedTexts])

  const isEdited = useCallback((cell: EditableCell): boolean => {
    const edited = editedTexts.get(cell.cellId)
    return edited !== undefined && edited !== cell.originalText
  }, [editedTexts])

  const changedCount = useMemo(() => {
    let count = 0
    for (const cell of cells) {
      if (isEdited(cell)) count++
    }
    return count
  }, [cells, isEdited])

  // Identify empty required cells (EN or DE columns with no text)
  const emptyCellIds = useMemo(() => {
    const required = new Set(['column_en', 'column_de'])
    const ids = new Set<string>()
    for (const cell of cells) {
      if (required.has(cell.colType) && !cell.text.trim()) {
        ids.add(cell.cellId)
      }
    }
    return ids
  }, [cells])

  // Sort cells for tab navigation: by row, then by column
  const sortedCellIds = useMemo(() => {
    return [...cells]
      .sort((a, b) => a.rowIndex !== b.rowIndex ? a.rowIndex - b.rowIndex : a.colIndex - b.colIndex)
      .map(c => c.cellId)
  }, [cells])

  const handleKeyDown = useCallback((e: React.KeyboardEvent, cellId: string) => {
    if (e.key === 'Tab') {
      e.preventDefault()
      const idx = sortedCellIds.indexOf(cellId)
      const nextIdx = e.shiftKey ? idx - 1 : idx + 1
      if (nextIdx >= 0 && nextIdx < sortedCellIds.length) {
        const nextId = sortedCellIds[nextIdx]
        const el = document.getElementById(`cell-${nextId}`)
        el?.focus()
      }
    }
  }, [sortedCellIds])

  const saveReconstruction = useCallback(async () => {
    if (!sessionId) return
    setStatus('saving')
    try {
      const cellUpdates = Array.from(editedTexts.entries())
        .filter(([cellId, text]) => {
          const cell = cells.find(c => c.cellId === cellId)
          return cell && text !== cell.originalText
        })
        .map(([cellId, text]) => ({ cell_id: cellId, text }))

      if (cellUpdates.length === 0) {
        // Nothing changed, just advance
        setStatus('saved')
        return
      }

      const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/reconstruction`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ cells: cellUpdates }),
      })

      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error(data.detail || `HTTP ${res.status}`)
      }

      setStatus('saved')
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : String(e))
      setStatus('error')
    }
  }, [sessionId, editedTexts, cells])

  // Handler for Fabric.js editor cell changes
  const handleFabricCellsChanged = useCallback((updates: { cell_id: string; text: string }[]) => {
    for (const u of updates) {
      setEditedTexts(prev => {
        const next = new Map(prev)
        next.set(u.cell_id, u.text)
        return next
      })
    }
  }, [])

  const dewarpedUrl = sessionId
    ? `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/cropped`
    : ''

  const colTypeColor = (colType: string): string => {
    const colors: Record<string, string> = {
      column_en: 'border-blue-400/40 focus:border-blue-500',
      column_de: 'border-green-400/40 focus:border-green-500',
      column_example: 'border-orange-400/40 focus:border-orange-500',
      column_text: 'border-purple-400/40 focus:border-purple-500',
      page_ref: 'border-cyan-400/40 focus:border-cyan-500',
      column_marker: 'border-gray-400/40 focus:border-gray-500',
    }
    return colors[colType] || 'border-gray-400/40 focus:border-gray-500'
  }

  // Font size based on image natural height (not container) scaled by zoom
  const getFontSize = useCallback((bboxH: number): number => {
    const baseH = imageNaturalH || 800
    const px = (bboxH / 100) * baseH * 0.55
    return Math.max(8, Math.min(18, px * (zoom / 100)))
  }, [imageNaturalH, zoom])

  // Box zones in percent for clamping cell positions in overlay mode
  const boxZonesPct = useMemo(() =>
    parentZones
      .filter(z => z.zone_type === 'box' && z.box)
      .map(z => {
        const imgH = imageNaturalSize?.h || 1
        return {
          topPct: (z.box!.y / imgH) * 100,
          bottomPct: ((z.box!.y + z.box!.height) / imgH) * 100,
        }
      }),
    [parentZones, imageNaturalSize]
  )

  if (!sessionId) {
    return <div className="text-center py-12 text-gray-400">Bitte zuerst eine Session auswaehlen.</div>
  }

  if (status === 'loading') {
    return (
      <div className="flex items-center gap-3 justify-center py-12">
        <div className="animate-spin rounded-full h-5 w-5 border-b-2 border-teal-500" />
        <span className="text-gray-500">Rekonstruktionsdaten werden geladen...</span>
      </div>
    )
  }

  if (status === 'error') {
    return (
      <div className="flex flex-col items-center justify-center py-12 text-center">
        <div className="text-5xl mb-4">&#x26A0;&#xFE0F;</div>
        <h3 className="text-lg font-medium text-red-600 dark:text-red-400 mb-2">Fehler</h3>
        <p className="text-sm text-gray-500 dark:text-gray-400 max-w-lg mb-4">{error}</p>
        <div className="flex gap-3">
          <button onClick={() => { setError(''); loadSessionData() }}
            className="px-5 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 transition-colors text-sm">
            Erneut versuchen
          </button>
          <button onClick={onNext}
            className="px-5 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors text-sm">
            Ueberspringen &rarr;
          </button>
        </div>
      </div>
    )
  }

  if (status === 'saved') {
    return (
      <div className="flex flex-col items-center justify-center py-12 text-center">
        <div className="text-5xl mb-4">&#x2705;</div>
        <h3 className="text-lg font-medium text-gray-700 dark:text-gray-300 mb-2">Rekonstruktion gespeichert</h3>
        <p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
          {changedCount > 0 ? `${changedCount} Zellen wurden aktualisiert.` : 'Keine Aenderungen vorgenommen.'}
        </p>
        <button onClick={onNext}
          className="px-6 py-2.5 bg-teal-600 text-white rounded-lg hover:bg-teal-700 transition-colors font-medium">
          Weiter &rarr;
        </button>
      </div>
    )
  }

  // Clamp cell positions so they don't overlap with box zones
  const adjustCellForBoxZones = (
    bboxPct: { x: number; y: number; w: number; h: number },
    cellId: string,
  ): { x: number; y: number; w: number; h: number } => {
    // Sub-session cells (inside box) → no adjustment
    if (cellId.startsWith('sub_')) return bboxPct
    if (boxZonesPct.length === 0) return bboxPct

    const cellTop = bboxPct.y
    const cellBottom = bboxPct.y + bboxPct.h
    const boxMid = (boxZonesPct[0].topPct + boxZonesPct[0].bottomPct) / 2

    for (const { topPct, bottomPct } of boxZonesPct) {
      // Check if cell overlaps with box zone at all
      if (cellBottom <= topPct || cellTop >= bottomPct) continue

      // Cell starts ABOVE box midpoint → belongs above, clamp bottom to box top
      if (cellTop < boxMid) {
        return { ...bboxPct, h: Math.max(0.5, topPct - cellTop) }
      }
      // Cell starts AT or BELOW box midpoint → belongs below, push top to box bottom
      return { ...bboxPct, y: bottomPct, h: Math.max(0.5, cellBottom - bottomPct) }
    }
    return bboxPct
  }

  // Structure layer: boxes and graphic elements as background
  const renderStructureLayer = (imgW: number, imgH: number) => {
    if (!showStructure) return null
    const hasElements = structureBoxes.length > 0 || structureGraphics.length > 0
    if (!hasElements) return null

    return (
      <>
        {/* Structure boxes */}
        {structureBoxes.map((box, i) => {
          const bgColor = box.bg_color_hex || '#6b7280'
          return (
            <div
              key={`sbox-${i}`}
              className="absolute pointer-events-none"
              style={{
                left: `${(box.x / imgW) * 100}%`,
                top: `${(box.y / imgH) * 100}%`,
                width: `${(box.w / imgW) * 100}%`,
                height: `${(box.h / imgH) * 100}%`,
                border: `${Math.max(1, box.border_thickness)}px solid ${bgColor}40`,
                backgroundColor: `${bgColor}0a`,
                borderRadius: '2px',
              }}
            />
          )
        })}

        {/* Graphic elements */}
        {structureGraphics.map((g, i) => (
          <div
            key={`sgfx-${i}`}
            className="absolute pointer-events-none"
            style={{
              left: `${(g.x / imgW) * 100}%`,
              top: `${(g.y / imgH) * 100}%`,
              width: `${(g.w / imgW) * 100}%`,
              height: `${(g.h / imgH) * 100}%`,
              border: `1px dashed ${g.color_hex}60`,
              backgroundColor: `${g.color_hex}08`,
              borderRadius: '2px',
            }}
          >
            <span
              className="absolute text-[8px] leading-none opacity-50"
              style={{
                top: '1px',
                left: '2px',
                color: g.color_hex,
              }}
            >
              {g.shape === 'illustration' ? 'Illust' : 'Bild'}
            </span>
          </div>
        ))}
      </>
    )
  }

  // Overlay rendering helper
  const renderOverlayMode = () => {
    const imgW = imageNaturalSize?.w || 1
    const imgH = imageNaturalSize?.h || 1
    const aspect = imgH / imgW
    const containerH = reconWidth * aspect

    return (
      <div className="grid grid-cols-2 gap-4">
        {/* Left: Original image */}
        <div>
          <div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
            Originalbild
          </div>
          <div className="border rounded-lg overflow-hidden dark:border-gray-700 bg-gray-50 dark:bg-gray-900 sticky top-4">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={dewarpedUrl}
              alt="Original"
              className="w-full h-auto"
              onLoad={(e) => {
                const img = e.target as HTMLImageElement
                setImageNaturalSize({ w: img.naturalWidth, h: img.naturalHeight })
              }}
            />
          </div>
        </div>

        {/* Right: Reconstructed table overlay */}
        <div>
          <div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
            Rekonstruktion ({cells.length} Zellen)
          </div>
          <div className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white">
            <div
              ref={reconRef}
              className="relative"
              style={{
                aspectRatio: `${imgW} / ${imgH}`,
              }}
            >
              {/* Column lines */}
              {parentColumns
                .filter(c => !['header', 'footer'].includes(c.type))
                .map((col, i) => (
                  <div
                    key={`col-${i}`}
                    className="absolute top-0 bottom-0 border-l border-gray-300/50"
                    style={{ left: `${(col.x / imgW) * 100}%` }}
                  />
                ))}

              {/* Row lines */}
              {parentRows.map((row, i) => (
                <div
                  key={`row-${i}`}
                  className="absolute left-0 right-0 border-t border-gray-300/50"
                  style={{ top: `${(row.y / imgH) * 100}%` }}
                />
              ))}

              {/* Box zone highlight */}
              {parentZones
                .filter(z => z.zone_type === 'box' && z.box)
                .map((z, i) => {
                  const box = z.box!
                  return (
                    <div
                      key={`box-${i}`}
                      className="absolute border-2 border-blue-400/30 bg-blue-50/10 pointer-events-none"
                      style={{
                        left: `${(box.x / imgW) * 100}%`,
                        top: `${(box.y / imgH) * 100}%`,
                        width: `${(box.width / imgW) * 100}%`,
                        height: `${(box.height / imgH) * 100}%`,
                      }}
                    />
                  )
                })}

              {/* Structure elements (boxes, graphics) */}
              {renderStructureLayer(imgW, imgH)}

              {/* Pixel-positioned words / editable inputs */}
              {cells.map((cell) => {
                const displayText = getDisplayText(cell)
                const edited = isEdited(cell)
                const wordPos = cellWordPositions.get(cell.cellId)
                const adjBbox = adjustCellForBoxZones(cell.bboxPct, cell.cellId)
                const cellHeightPx = containerH * (adjBbox.h / 100)

                // Pixel-analysed: render word-groups at detected positions as inputs
                if (wordPos && wordPos.length > 0) {
                  return wordPos.map((wp, i) => {
                    const autoFontPx = cellHeightPx * wp.fontRatio * fontScale
                    const fs = Math.max(6, autoFontPx)

                    // For multi-group cells, only the first group is the primary input
                    // Show as span (read-only positioned) — editing happens at cell level
                    if (wordPos.length > 1) {
                      return (
                        <span
                          key={`${cell.cellId}_wp_${i}`}
                          className="absolute leading-none pointer-events-none select-none"
                          style={{
                            left: `${wp.xPct}%`,
                            top: `${adjBbox.y}%`,
                            width: `${wp.wPct}%`,
                            height: `${adjBbox.h}%`,
                            fontSize: `${fs}px`,
                            fontWeight: globalBold ? 'bold' : (cell.colType === 'column_en' ? 'bold' : 'normal'),
                            fontFamily: "'Liberation Sans', Arial, sans-serif",
                            display: 'flex',
                            alignItems: 'center',
                            whiteSpace: 'nowrap',
                            overflow: 'visible',
                            color: '#1a1a1a',
                          }}
                        >
                          {wp.text}
                        </span>
                      )
                    }

                    // Single group: render as editable input at pixel position
                    return (
                      <div key={`${cell.cellId}_wp_${i}`} className="absolute group" style={{
                        left: `${wp.xPct}%`,
                        top: `${adjBbox.y}%`,
                        width: `${wp.wPct}%`,
                        height: `${adjBbox.h}%`,
                      }}>
                        <input
                          id={`cell-${cell.cellId}`}
                          type="text"
                          value={displayText}
                          onChange={(e) => handleTextChange(cell.cellId, e.target.value)}
                          onKeyDown={(e) => handleKeyDown(e, cell.cellId)}
                          className={`w-full h-full bg-transparent border-0 outline-none px-0 transition-colors ${
                            edited ? 'bg-green-50/30' : ''
                          }`}
                          style={{
                            fontSize: `${fs}px`,
                            fontWeight: globalBold ? 'bold' : (cell.colType === 'column_en' ? 'bold' : 'normal'),
                            fontFamily: "'Liberation Sans', Arial, sans-serif",
                            lineHeight: '1',
                            color: '#1a1a1a',
                          }}
                          title={`${cell.cellId} (${cell.colType})`}
                        />
                        {edited && (
                          <button
                            onClick={() => resetCell(cell.cellId)}
                            className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full text-[9px] leading-none opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
                            title="Zuruecksetzen"
                          >
                            &times;
                          </button>
                        )}
                      </div>
                    )
                  })
                }

                // Multi-group cell with pixel positions: already handled above
                // Fallback: no pixel data — single input at cell bbox
                if (!cell.text) return null

                const fontSize = Math.max(6, cellHeightPx * fontScale)
                return (
                  <div key={cell.cellId} className="absolute group" style={{
                    left: `${adjBbox.x}%`,
                    top: `${adjBbox.y}%`,
                    width: `${adjBbox.w}%`,
                    height: `${adjBbox.h}%`,
                  }}>
                    <input
                      id={`cell-${cell.cellId}`}
                      type="text"
                      value={displayText}
                      onChange={(e) => handleTextChange(cell.cellId, e.target.value)}
                      onKeyDown={(e) => handleKeyDown(e, cell.cellId)}
                      className={`w-full h-full bg-transparent border-0 outline-none px-0 transition-colors ${
                        edited ? 'bg-green-50/30' : ''
                      }`}
                      style={{
                        fontSize: `${fontSize}px`,
                        fontWeight: globalBold ? 'bold' : 'normal',
                        fontFamily: "'Liberation Sans', Arial, sans-serif",
                        lineHeight: '1',
                        color: '#1a1a1a',
                      }}
                      title={`${cell.cellId} (${cell.colType})`}
                    />
                    {edited && (
                      <button
                        onClick={() => resetCell(cell.cellId)}
                        className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full text-[9px] leading-none opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
                        title="Zuruecksetzen"
                      >
                        &times;
                      </button>
                    )}
                  </div>
                )
              })}
            </div>
          </div>
        </div>
      </div>
    )
  }

  return (
    <div className="space-y-3">
      {/* Toolbar */}
      <div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 px-3 py-2">
        <div className="flex items-center gap-2">
          <h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">
            Schritt 7: Rekonstruktion
          </h3>
          {/* Mode toggle */}
          <div className="flex items-center ml-2 border border-gray-300 dark:border-gray-600 rounded overflow-hidden text-xs">
            <button
              onClick={() => setEditorMode('simple')}
              className={`px-2 py-0.5 transition-colors ${
                editorMode === 'simple'
                  ? 'bg-teal-600 text-white'
                  : 'hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
              }`}
            >
              Einfach
            </button>
            {isParentWithBoxes && (
              <button
                onClick={() => setEditorMode('overlay')}
                className={`px-2 py-0.5 transition-colors ${
                  editorMode === 'overlay'
                    ? 'bg-teal-600 text-white'
                    : 'hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
                }`}
              >
                Overlay
              </button>
            )}
            <button
              onClick={() => setEditorMode('editor')}
              className={`px-2 py-0.5 transition-colors ${
                editorMode === 'editor'
                  ? 'bg-teal-600 text-white'
                  : 'hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
              }`}
            >
              Editor
            </button>
          </div>
          <span className="text-xs text-gray-400">
            {cells.length} Zellen &middot; {changedCount} geaendert
            {emptyCellIds.size > 0 && showEmptyHighlight && (
              <span className="text-red-400 ml-1">&middot; {emptyCellIds.size} leer</span>
            )}
          </span>
        </div>
        <div className="flex items-center gap-2">
          {/* Undo/Redo */}
          <button
            onClick={undo}
            disabled={undoStack.length === 0}
            className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-30"
            title="Rueckgaengig (Ctrl+Z)"
          >
            &#x21A9;
          </button>
          <button
            onClick={redo}
            disabled={redoStack.length === 0}
            className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-30"
            title="Wiederholen (Ctrl+Shift+Z)"
          >
            &#x21AA;
          </button>

          <div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />

          {/* Overlay-specific toolbar */}
          {editorMode === 'overlay' && (
            <>
              <label className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400">
                Schrift
                <input
                  type="range" min={30} max={120} value={Math.round(fontScale * 100)}
                  onChange={e => setFontScale(Number(e.target.value) / 100)}
                  className="w-20 h-1 accent-teal-600"
                />
                <span className="w-8 text-right font-mono">{Math.round(fontScale * 100)}%</span>
              </label>
              <button
                onClick={() => setGlobalBold(b => !b)}
                className={`px-2 py-1 text-xs rounded border transition-colors font-bold ${
                  globalBold
                    ? 'bg-teal-600 text-white border-teal-600'
                    : 'bg-white dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-300 dark:border-gray-600'
                }`}
              >
                B
              </button>
              <button
                onClick={() => setImageRotation(r => r === 0 ? 180 : 0)}
                className={`px-2 py-1 text-xs rounded border transition-colors ${
                  imageRotation === 180
                    ? 'bg-teal-600 text-white border-teal-600'
                    : 'bg-white dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-300 dark:border-gray-600'
                }`}
                title="Bild 180° drehen"
              >
                180°
              </button>
              {(structureBoxes.length > 0 || structureGraphics.length > 0) && (
                <button
                  onClick={() => setShowStructure(v => !v)}
                  className={`px-2 py-1 text-xs border rounded transition-colors ${
                    showStructure
                      ? 'border-violet-300 bg-violet-50 text-violet-600 dark:border-violet-700 dark:bg-violet-900/30 dark:text-violet-400'
                      : 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
                  }`}
                  title="Strukturelemente anzeigen"
                >
                  Struktur
                </button>
              )}
              <div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
            </>
          )}

          {/* Non-overlay controls */}
          {editorMode !== 'overlay' && (
            <>
              {/* Empty field toggle */}
              <button
                onClick={() => setShowEmptyHighlight(v => !v)}
                className={`px-2 py-1 text-xs border rounded transition-colors ${
                  showEmptyHighlight
                    ? 'border-red-300 bg-red-50 text-red-600 dark:border-red-700 dark:bg-red-900/30 dark:text-red-400'
                    : 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
                }`}
                title="Leere Pflichtfelder markieren"
              >
                Leer
              </button>

              {/* Structure toggle */}
              {(structureBoxes.length > 0 || structureGraphics.length > 0) && (
                <button
                  onClick={() => setShowStructure(v => !v)}
                  className={`px-2 py-1 text-xs border rounded transition-colors ${
                    showStructure
                      ? 'border-violet-300 bg-violet-50 text-violet-600 dark:border-violet-700 dark:bg-violet-900/30 dark:text-violet-400'
                      : 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
                  }`}
                  title="Strukturelemente anzeigen"
                >
                  Struktur
                </button>
              )}

              <div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />

              {/* Zoom controls */}
              <button
                onClick={() => setZoom(z => Math.max(50, z - 25))}
                className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700"
              >
                &minus;
              </button>
              <span className="text-xs text-gray-500 w-10 text-center">{zoom}%</span>
              <button
                onClick={() => setZoom(z => Math.min(200, z + 25))}
                className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700"
              >
                +
              </button>
              <button
                onClick={() => setZoom(100)}
                className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700"
              >
                Fit
              </button>

              <div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
            </>
          )}

          <button
            onClick={saveReconstruction}
            disabled={status === 'saving'}
            className="px-4 py-1.5 text-xs bg-teal-600 text-white rounded-lg hover:bg-teal-700 disabled:opacity-50 transition-colors font-medium"
          >
            {status === 'saving' ? 'Speichert...' : 'Speichern'}
          </button>
        </div>
      </div>

      {/* Reconstruction canvas */}
      {editorMode === 'overlay' ? (
        renderOverlayMode()
      ) : editorMode === 'editor' && sessionId ? (
        <FabricReconstructionCanvas
          sessionId={sessionId}
          cells={gridCells}
          onCellsChanged={handleFabricCellsChanged}
        />
      ) : (
        <div className="border rounded-lg overflow-auto dark:border-gray-700 bg-gray-100 dark:bg-gray-900" style={{ maxHeight: '75vh' }}>
          <div
            ref={containerRef}
            className="relative inline-block"
            style={{ transform: `scale(${zoom / 100})`, transformOrigin: 'top left' }}
          >
            {/* Background image at reduced opacity */}
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              ref={imageRef}
              src={dewarpedUrl}
              alt="Dewarped"
              className="block"
              style={{ opacity: 0.3 }}
              onLoad={handleImageLoad}
            />

            {/* Structure elements (boxes, graphics) */}
            {imageNaturalSize && renderStructureLayer(imageNaturalSize.w, imageNaturalSize.h)}

            {/* Empty field markers */}
            {showEmptyHighlight && cells
              .filter(c => emptyCellIds.has(c.cellId))
              .map(cell => (
                <div
                  key={`empty-${cell.cellId}`}
                  className="absolute border-2 border-dashed border-red-400/60 rounded pointer-events-none"
                  style={{
                    left: `${cell.bboxPct.x}%`,
                    top: `${cell.bboxPct.y}%`,
                    width: `${cell.bboxPct.w}%`,
                    height: `${cell.bboxPct.h}%`,
                  }}
                />
              ))}

            {/* Editable text fields at bbox positions */}
            {cells.map((cell) => {
              const displayText = getDisplayText(cell)
              const edited = isEdited(cell)

              return (
                <div key={cell.cellId} className="absolute group" style={{
                  left: `${cell.bboxPct.x}%`,
                  top: `${cell.bboxPct.y}%`,
                  width: `${cell.bboxPct.w}%`,
                  height: `${cell.bboxPct.h}%`,
                }}>
                  <input
                    id={`cell-${cell.cellId}`}
                    type="text"
                    value={displayText}
                    onChange={(e) => handleTextChange(cell.cellId, e.target.value)}
                    onKeyDown={(e) => handleKeyDown(e, cell.cellId)}
                    className={`w-full h-full bg-transparent text-black dark:text-white border px-0.5 outline-none transition-colors ${
                      colTypeColor(cell.colType)
                    } ${edited ? 'border-green-500 bg-green-50/30 dark:bg-green-900/20' : ''}`}
                    style={{
                      fontSize: `${getFontSize(cell.bboxPct.h)}px`,
                      lineHeight: '1',
                    }}
                    title={`${cell.cellId} (${cell.colType})`}
                  />
                  {/* Per-cell reset button (X) — only shown for edited cells on hover */}
                  {edited && (
                    <button
                      onClick={() => resetCell(cell.cellId)}
                      className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full text-[9px] leading-none opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
                      title="Zuruecksetzen"
                    >
                      &times;
                    </button>
                  )}
                </div>
              )
            })}
          </div>
        </div>
      )}

      {/* Bottom action */}
      <div className="flex justify-end">
        <button
          onClick={() => {
            if (changedCount > 0) {
              saveReconstruction()
            } else {
              onNext()
            }
          }}
          className="px-6 py-2.5 bg-teal-600 text-white rounded-lg hover:bg-teal-700 transition-colors font-medium text-sm"
        >
          {changedCount > 0 ? 'Speichern & Weiter \u2192' : 'Weiter \u2192'}
        </button>
      </div>
    </div>
  )
}
