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

interface ActionDeps {
  grid: StructuredGrid | null
  setGrid: React.Dispatch<React.SetStateAction<StructuredGrid | null>>
  setDirty: React.Dispatch<React.SetStateAction<boolean>>
  pushUndo: (zones: GridZone[]) => void
}

export function useGridEditorActions(deps: ActionDeps) {
  const { grid, setGrid, setDirty, pushUndo } = deps

  // ------------------------------------------------------------------
  // Multi-select state
  // ------------------------------------------------------------------

  const [selectedCells, setSelectedCells] = useState<Set<string>>(new Set())

  const toggleCellSelection = useCallback(
    (cellId: string) => {
      setSelectedCells((prev) => {
        const next = new Set(prev)
        if (next.has(cellId)) next.delete(cellId)
        else next.add(cellId)
        return next
      })
    },
    [],
  )

  const clearCellSelection = useCallback(() => {
    setSelectedCells(new Set())
  }, [])

  // ------------------------------------------------------------------
  // 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) => {
            // Check if cell exists
            const existing = zone.cells.find((c) => c.cell_id === cellId)
            if (existing) {
              return {
                ...zone,
                cells: zone.cells.map((cell) =>
                  cell.cell_id === cellId ? { ...cell, text: newText } : cell,
                ),
              }
            }
            // Cell doesn't exist — create it if the cellId belongs to this zone
            // cellId format: Z{zone}_R{row}_C{col}
            const match = cellId.match(/^Z(\d+)_R(\d+)_C(\d+)$/)
            if (!match || parseInt(match[1]) !== zone.zone_index) return zone
            const rowIndex = parseInt(match[2])
            const colIndex = parseInt(match[3])
            const col = zone.columns.find((c) => c.index === colIndex)
            const newCell = {
              cell_id: cellId,
              zone_index: zone.zone_index,
              row_index: rowIndex,
              col_index: colIndex,
              col_type: col?.label ?? '',
              text: newText,
              confidence: 0,
              bbox_px: { x: 0, y: 0, w: 0, h: 0 },
              bbox_pct: { x: 0, y: 0, w: 0, h: 0 },
              word_boxes: [],
              ocr_engine: 'manual',
              is_bold: false,
            }
            return { ...zone, cells: [...zone.cells, newCell] }
          }),
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  // ------------------------------------------------------------------
  // Cell color
  // ------------------------------------------------------------------

  /**
   * Set a manual color override on a cell.
   * - hex string (e.g. "#dc2626"): force text color
   * - null: force no color (clear bar)
   * - undefined: remove override, restore OCR-detected color
   */
  const setCellColor = useCallback(
    (cellId: string, color: string | null | undefined) => {
      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) => {
              if (cell.cell_id !== cellId) return cell
              if (color === undefined) {
                // Remove override entirely — restore OCR behavior
                const { color_override: _, ...rest } = cell
                return rest
              }
              return { ...cell, color_override: color }
            }),
          })),
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  // ------------------------------------------------------------------
  // 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, setGrid, setDirty],
  )

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

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

      // Cycle: normal -> header -> footer -> normal
      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) => {
                if (r.index !== rowIndex) return r
                if (!r.is_header && !r.is_footer) {
                  return { ...r, is_header: true, is_footer: false }
                } else if (r.is_header) {
                  return { ...r, is_header: false, is_footer: true }
                } else {
                  return { ...r, is_header: false, is_footer: false }
                }
              }),
            }
          }),
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  // ------------------------------------------------------------------
  // Column pattern auto-correction
  // ------------------------------------------------------------------

  /**
   * Detect dominant prefix+number patterns per column and complete
   * partial matches.  E.g. if 3+ cells read "p.70", "p.71", etc.,
   * a cell reading ".65" is corrected to "p.65".
   * Returns the number of corrections made.
   */
  const autoCorrectColumnPatterns = useCallback(() => {
    if (!grid) return 0
    pushUndo(grid.zones)

    let totalFixed = 0
    const numberPattern = /^(.+?)(\d+)\s*$/

    setGrid((prev) => {
      if (!prev) return prev
      return {
        ...prev,
        zones: prev.zones.map((zone) => {
          // Group cells by column
          const cellsByCol = new Map<number, { cell: (typeof zone.cells)[0]; idx: number }[]>()
          zone.cells.forEach((cell, idx) => {
            const arr = cellsByCol.get(cell.col_index) || []
            arr.push({ cell, idx })
            cellsByCol.set(cell.col_index, arr)
          })

          const newCells = [...zone.cells]

          for (const [, colEntries] of cellsByCol) {
            // Count prefix occurrences
            const prefixCounts = new Map<string, number>()
            for (const { cell } of colEntries) {
              const m = cell.text.trim().match(numberPattern)
              if (m) {
                prefixCounts.set(m[1], (prefixCounts.get(m[1]) || 0) + 1)
              }
            }

            // Find dominant prefix (>= 3 occurrences)
            let dominantPrefix = ''
            let maxCount = 0
            for (const [prefix, count] of prefixCounts) {
              if (count >= 3 && count > maxCount) {
                dominantPrefix = prefix
                maxCount = count
              }
            }
            if (!dominantPrefix) continue

            // Fix partial matches — entries that are just [.?\s*]NUMBER
            for (const { cell, idx } of colEntries) {
              const text = cell.text.trim()
              if (!text || text.startsWith(dominantPrefix)) continue

              const numMatch = text.match(/^[.\s]*(\d+)\s*$/)
              if (numMatch) {
                newCells[idx] = { ...newCells[idx], text: `${dominantPrefix}${numMatch[1]}` }
                totalFixed++
              }
            }
          }

          return { ...zone, cells: newCells }
        }),
      }
    })

    if (totalFixed > 0) setDirty(true)
    return totalFixed
  }, [grid, pushUndo, setGrid, setDirty])

  // ------------------------------------------------------------------
  // Multi-select bulk formatting
  // ------------------------------------------------------------------

  /** Toggle bold on all selected cells (and their columns). */
  const toggleSelectedBold = useCallback(() => {
    if (!grid || selectedCells.size === 0) return
    pushUndo(grid.zones)

    // Determine if we're turning bold on or off (majority rule)
    const cells = grid.zones.flatMap((z) => z.cells)
    const selectedArr = cells.filter((c) => selectedCells.has(c.cell_id))
    const boldCount = selectedArr.filter((c) => c.is_bold).length
    const newBold = boldCount < selectedArr.length / 2

    setGrid((prev) => {
      if (!prev) return prev
      return {
        ...prev,
        zones: prev.zones.map((zone) => ({
          ...zone,
          cells: zone.cells.map((cell) =>
            selectedCells.has(cell.cell_id) ? { ...cell, is_bold: newBold } : cell,
          ),
        })),
      }
    })
    setDirty(true)
    setSelectedCells(new Set())
  }, [grid, selectedCells, pushUndo, setGrid, setDirty])

  return {
    // Cell editing
    updateCellText,
    setCellColor,
    // Column formatting
    toggleColumnBold,
    // Row formatting
    toggleRowHeader,
    // Pattern correction
    autoCorrectColumnPatterns,
    // Multi-select
    selectedCells,
    toggleCellSelection,
    clearCellSelection,
    toggleSelectedBold,
  }
}
