import { useCallback, useRef, useState } from 'react'
import type { StructuredGrid, GridZone } from './types'

const KLAUSUR_API = '/klausur-api'
const MAX_UNDO = 50

export interface GridEditorState {
  grid: StructuredGrid | null
  loading: boolean
  saving: boolean
  error: string | null
  dirty: boolean
  selectedCell: string | null
  selectedZone: number | null
}

export function useGridEditor(sessionId: string | null) {
  const [grid, setGrid] = useState<StructuredGrid | null>(null)
  const [loading, setLoading] = useState(false)
  const [saving, setSaving] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [dirty, setDirty] = useState(false)
  const [selectedCell, setSelectedCell] = useState<string | null>(null)
  const [selectedZone, setSelectedZone] = useState<number | null>(null)

  // Undo/redo stacks store serialized zone arrays
  const undoStack = useRef<string[]>([])
  const redoStack = useRef<string[]>([])

  const pushUndo = useCallback((zones: GridZone[]) => {
    undoStack.current.push(JSON.stringify(zones))
    if (undoStack.current.length > MAX_UNDO) {
      undoStack.current.shift()
    }
    redoStack.current = []
  }, [])

  // ------------------------------------------------------------------
  // Load / Build
  // ------------------------------------------------------------------

  const buildGrid = useCallback(async () => {
    if (!sessionId) return
    setLoading(true)
    setError(null)
    try {
      const res = await fetch(
        `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/build-grid`,
        { method: 'POST' },
      )
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error(data.detail || `HTTP ${res.status}`)
      }
      const data: StructuredGrid = await res.json()
      setGrid(data)
      setDirty(false)
      undoStack.current = []
      redoStack.current = []
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e))
    } finally {
      setLoading(false)
    }
  }, [sessionId])

  const loadGrid = useCallback(async () => {
    if (!sessionId) return
    setLoading(true)
    setError(null)
    try {
      const res = await fetch(
        `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/grid-editor`,
      )
      if (res.status === 404) {
        // No grid yet — build it
        await buildGrid()
        return
      }
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error(data.detail || `HTTP ${res.status}`)
      }
      const data: StructuredGrid = await res.json()
      setGrid(data)
      setDirty(false)
      undoStack.current = []
      redoStack.current = []
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e))
    } finally {
      setLoading(false)
    }
  }, [sessionId, buildGrid])

  // ------------------------------------------------------------------
  // Save
  // ------------------------------------------------------------------

  const saveGrid = useCallback(async () => {
    if (!sessionId || !grid) return
    setSaving(true)
    try {
      const res = await fetch(
        `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/save-grid`,
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(grid),
        },
      )
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error(data.detail || `HTTP ${res.status}`)
      }
      setDirty(false)
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e))
    } finally {
      setSaving(false)
    }
  }, [sessionId, grid])

  // ------------------------------------------------------------------
  // Cell editing
  // ------------------------------------------------------------------

  const updateCellText = useCallback(
    (cellId: string, newText: string) => {
      if (!grid) return
      pushUndo(grid.zones)

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((zone) => ({
            ...zone,
            cells: zone.cells.map((cell) =>
              cell.cell_id === cellId ? { ...cell, text: newText } : cell,
            ),
          })),
        }
      })
      setDirty(true)
    },
    [grid, pushUndo],
  )

  // ------------------------------------------------------------------
  // Column formatting
  // ------------------------------------------------------------------

  const toggleColumnBold = useCallback(
    (zoneIndex: number, colIndex: number) => {
      if (!grid) return
      pushUndo(grid.zones)

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((zone) => {
            if (zone.zone_index !== zoneIndex) return zone
            const col = zone.columns.find((c) => c.index === colIndex)
            const newBold = col ? !col.bold : true
            return {
              ...zone,
              columns: zone.columns.map((c) =>
                c.index === colIndex ? { ...c, bold: newBold } : c,
              ),
              cells: zone.cells.map((cell) =>
                cell.col_index === colIndex
                  ? { ...cell, is_bold: newBold }
                  : cell,
              ),
            }
          }),
        }
      })
      setDirty(true)
    },
    [grid, pushUndo],
  )

  // ------------------------------------------------------------------
  // Row formatting
  // ------------------------------------------------------------------

  const toggleRowHeader = useCallback(
    (zoneIndex: number, rowIndex: number) => {
      if (!grid) return
      pushUndo(grid.zones)

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((zone) => {
            if (zone.zone_index !== zoneIndex) return zone
            return {
              ...zone,
              rows: zone.rows.map((r) =>
                r.index === rowIndex ? { ...r, is_header: !r.is_header } : r,
              ),
            }
          }),
        }
      })
      setDirty(true)
    },
    [grid, pushUndo],
  )

  // ------------------------------------------------------------------
  // Undo / Redo
  // ------------------------------------------------------------------

  const undo = useCallback(() => {
    if (!grid || undoStack.current.length === 0) return
    redoStack.current.push(JSON.stringify(grid.zones))
    const prev = undoStack.current.pop()!
    setGrid((g) => (g ? { ...g, zones: JSON.parse(prev) } : g))
    setDirty(true)
  }, [grid])

  const redo = useCallback(() => {
    if (!grid || redoStack.current.length === 0) return
    undoStack.current.push(JSON.stringify(grid.zones))
    const next = redoStack.current.pop()!
    setGrid((g) => (g ? { ...g, zones: JSON.parse(next) } : g))
    setDirty(true)
  }, [grid])

  const canUndo = undoStack.current.length > 0
  const canRedo = redoStack.current.length > 0

  // ------------------------------------------------------------------
  // Navigation helpers
  // ------------------------------------------------------------------

  const getAdjacentCell = useCallback(
    (cellId: string, direction: 'up' | 'down' | 'left' | 'right'): string | null => {
      if (!grid) return null
      for (const zone of grid.zones) {
        const cell = zone.cells.find((c) => c.cell_id === cellId)
        if (!cell) continue

        let targetRow = cell.row_index
        let targetCol = cell.col_index
        if (direction === 'up') targetRow--
        if (direction === 'down') targetRow++
        if (direction === 'left') targetCol--
        if (direction === 'right') targetCol++

        const target = zone.cells.find(
          (c) => c.row_index === targetRow && c.col_index === targetCol,
        )
        return target?.cell_id ?? null
      }
      return null
    },
    [grid],
  )

  return {
    grid,
    loading,
    saving,
    error,
    dirty,
    selectedCell,
    selectedZone,
    setSelectedCell,
    setSelectedZone,
    buildGrid,
    loadGrid,
    saveGrid,
    updateCellText,
    toggleColumnBold,
    toggleRowHeader,
    undo,
    redo,
    canUndo,
    canRedo,
    getAdjacentCell,
  }
}
