'use client'

import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { usePixelWordPositions } from './usePixelWordPositions'
import { useSlideWordPositions } from './useSlideWordPositions'
import type {
  EditableCell,
  GridCellCompat,
  OverlayReconstructionState,
  OverlayStatus,
  PositioningMode,
  RowItemCompat,
  RowResultCompat,
  TextColor,
  UndoAction,
  WordResultData,
} from './overlay-reconstruction-types'
import { KLAUSUR_API } from './overlay-reconstruction-types'

/**
 * All state and logic for OverlayReconstruction, extracted as a custom hook.
 */
export function useOverlayReconstructionState(
  sessionId: string | null,
  wordResultOverride?: WordResultData,
): OverlayReconstructionState {
  const [status, setStatus] = useState<OverlayStatus>('loading')
  const [error, setError] = useState('')
  const [cells, setCells] = useState<EditableCell[]>([])
  const [gridCells, setGridCells] = useState<GridCellCompat[]>([])
  const [editedTexts, setEditedTexts] = useState<Map<string, string>>(new Map())

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

  // Overlay state
  const [rows, setRows] = useState<RowItemCompat[]>([])
  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 [textOpacity, setTextOpacity] = useState(100)
  const [textColor, setTextColor] = useState<TextColor>('red')
  const [positioningMode, setPositioningMode] = useState<PositioningMode>('slide')
  const reconRef = useRef<HTMLDivElement>(null)
  const [reconWidth, setReconWidth] = useState(0)

  // Pixel-based word positions
  const overlayImageUrl = sessionId
    ? `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/cropped`
    : ''
  const clusterPositions = usePixelWordPositions(
    overlayImageUrl,
    gridCells as never[],
    status === 'ready',
    imageRotation,
  )
  const slidePositions = useSlideWordPositions(
    overlayImageUrl,
    gridCells as never[],
    status === 'ready',
    imageRotation,
  )
  const cellWordPositions = positioningMode === 'slide' ? slidePositions : clusterPositions

  // Track container width
  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()
  }, [status])

  const applyWordResult = (wordResult: WordResultData) => {
    const rawGridCells: GridCellCompat[] = wordResult.cells || []
    setGridCells(rawGridCells)

    const editableCells: 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(editableCells)
    setEditedTexts(new Map())
    setUndoStack([])
    setRedoStack([])

    if (wordResult.image_width && wordResult.image_height) {
      setImageNaturalSize({ w: wordResult.image_width, h: wordResult.image_height })
    }

    setStatus('ready')
  }

  const loadSessionData = useCallback(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 = data.word_result
      if (!wordResult) {
        setError('Keine Worterkennungsdaten gefunden. Bitte zuerst den Woerter-Schritt abschliessen.')
        setStatus('error')
        return
      }

      applyWordResult(wordResult as WordResultData)

      const rowResult: RowResultCompat | undefined = data.row_result
      if (rowResult?.rows) setRows(rowResult.rows)
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : String(e))
      setStatus('error')
    }
  }, [sessionId])

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

  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 ?? ''

      setUndoStack(stack => [...stack, { cellId, oldText: prevText, newText }])
      setRedoStack([])

      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) => {
    setEditedTexts(prev => {
      const next = new Map(prev)
      next.delete(cellId)
      return next
    })
  }, [])

  // Keyboard shortcuts
  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])

  // Tab navigation
  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) {
        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])

  // Median cell height for consistent font sizing (must be before early returns)
  const medianCellHeightPx = useMemo(() => {
    const imgWVal = imageNaturalSize?.w || 1
    const imgHVal = imageNaturalSize?.h || 1
    const cH = reconWidth * (imgHVal / imgWVal)
    if (cells.length === 0 || cH === 0) return 40
    const heights = cells.map(c => cH * (c.bboxPct.h / 100)).sort((a, b) => a - b)
    const mid = Math.floor(heights.length / 2)
    return heights.length % 2 === 0 ? (heights[mid - 1] + heights[mid]) / 2 : heights[mid]
  }, [cells, reconWidth, imageNaturalSize])

  return {
    status,
    error,
    cells,
    gridCells,
    editedTexts,
    undoStack,
    redoStack,
    rows,
    imageNaturalSize,
    fontScale,
    globalBold,
    imageRotation,
    textOpacity,
    textColor,
    positioningMode,
    changedCount,
    sortedCellIds,
    medianCellHeightPx,
    cellWordPositions,
    reconRef,
    reconWidth,

    setFontScale,
    setGlobalBold,
    setImageRotation,
    setTextOpacity,
    setTextColor,
    setPositioningMode,
    handleTextChange,
    undo,
    redo,
    resetCell,
    handleKeyDown,
    getDisplayText,
    isEdited,
    saveReconstruction,
    loadSessionData,
    setError,
    setImageNaturalSize,
  }
}
