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

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

export function useGridEditorLayout(deps: LayoutDeps) {
  const { grid, setGrid, setDirty, pushUndo } = deps

  // ------------------------------------------------------------------
  // Column management
  // ------------------------------------------------------------------

  const deleteColumn = useCallback(
    (zoneIndex: number, colIndex: number) => {
      if (!grid) return
      const zone = grid.zones.find((z) => z.zone_index === zoneIndex)
      if (!zone || zone.columns.length <= 1) return // keep at least 1 column
      pushUndo(grid.zones)

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((z) => {
            if (z.zone_index !== zoneIndex) return z
            const deletedCol = z.columns.find((c) => c.index === colIndex)
            const newColumns = z.columns
              .filter((c) => c.index !== colIndex)
              .map((c, i) => {
                const result = { ...c, index: i, label: `column_${i + 1}` }
                // Merge x-boundary: previous column absorbs deleted column's space
                if (deletedCol) {
                  if (c.index === colIndex - 1) {
                    result.x_max_pct = deletedCol.x_max_pct
                    result.x_max_px = deletedCol.x_max_px
                  } else if (colIndex === 0 && c.index === 1) {
                    result.x_min_pct = deletedCol.x_min_pct
                    result.x_min_px = deletedCol.x_min_px
                  }
                }
                return result
              })
            const newCells = z.cells
              .filter((c) => c.col_index !== colIndex)
              .map((c) => {
                const newCI = c.col_index > colIndex ? c.col_index - 1 : c.col_index
                return {
                  ...c,
                  col_index: newCI,
                  cell_id: `Z${zoneIndex}_R${String(c.row_index).padStart(2, '0')}_C${newCI}`,
                }
              })
            return { ...z, columns: newColumns, cells: newCells }
          }),
          summary: {
            ...prev.summary,
            total_columns: prev.summary.total_columns - 1,
            total_cells: prev.zones.reduce(
              (sum, z) =>
                sum +
                (z.zone_index === zoneIndex
                  ? z.cells.filter((c) => c.col_index !== colIndex).length
                  : z.cells.length),
              0,
            ),
          },
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  const addColumn = useCallback(
    (zoneIndex: number, afterColIndex: number) => {
      if (!grid) return
      const zone = grid.zones.find((z) => z.zone_index === zoneIndex)
      if (!zone) return
      pushUndo(grid.zones)

      const newColIndex = afterColIndex + 1

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((z) => {
            if (z.zone_index !== zoneIndex) return z
            // Shift existing columns
            const shiftedCols = z.columns.map((c) =>
              c.index > afterColIndex ? { ...c, index: c.index + 1, label: `column_${c.index + 2}` } : c,
            )
            // Insert new column
            const refCol = z.columns.find((c) => c.index === afterColIndex) || z.columns[z.columns.length - 1]
            const newCol = {
              index: newColIndex,
              label: `column_${newColIndex + 1}`,
              x_min_px: refCol.x_max_px,
              x_max_px: refCol.x_max_px + (refCol.x_max_px - refCol.x_min_px),
              x_min_pct: refCol.x_max_pct,
              x_max_pct: Math.min(100, refCol.x_max_pct + (refCol.x_max_pct - refCol.x_min_pct)),
              bold: false,
            }
            const allCols = [...shiftedCols, newCol].sort((a, b) => a.index - b.index)

            // Shift existing cells and create empty cells for new column
            const shiftedCells = z.cells.map((c) => {
              if (c.col_index > afterColIndex) {
                const newCI = c.col_index + 1
                return {
                  ...c,
                  col_index: newCI,
                  cell_id: `Z${zoneIndex}_R${String(c.row_index).padStart(2, '0')}_C${newCI}`,
                }
              }
              return c
            })
            // Create empty cells for each row
            const newCells = z.rows.map((row) => ({
              cell_id: `Z${zoneIndex}_R${String(row.index).padStart(2, '0')}_C${newColIndex}`,
              zone_index: zoneIndex,
              row_index: row.index,
              col_index: newColIndex,
              col_type: `column_${newColIndex + 1}`,
              text: '',
              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 { ...z, columns: allCols, cells: [...shiftedCells, ...newCells] }
          }),
          summary: {
            ...prev.summary,
            total_columns: prev.summary.total_columns + 1,
            total_cells: prev.summary.total_cells + (zone?.rows.length ?? 0),
          },
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  // ------------------------------------------------------------------
  // Row management
  // ------------------------------------------------------------------

  const deleteRow = useCallback(
    (zoneIndex: number, rowIndex: number) => {
      if (!grid) return
      const zone = grid.zones.find((z) => z.zone_index === zoneIndex)
      if (!zone || zone.rows.length <= 1) return // keep at least 1 row
      pushUndo(grid.zones)

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((z) => {
            if (z.zone_index !== zoneIndex) return z
            const newRows = z.rows
              .filter((r) => r.index !== rowIndex)
              .map((r, i) => ({ ...r, index: i }))
            const newCells = z.cells
              .filter((c) => c.row_index !== rowIndex)
              .map((c) => {
                const newRI = c.row_index > rowIndex ? c.row_index - 1 : c.row_index
                return {
                  ...c,
                  row_index: newRI,
                  cell_id: `Z${zoneIndex}_R${String(newRI).padStart(2, '0')}_C${c.col_index}`,
                }
              })
            return { ...z, rows: newRows, cells: newCells }
          }),
          summary: {
            ...prev.summary,
            total_rows: prev.summary.total_rows - 1,
            total_cells: prev.zones.reduce(
              (sum, z) =>
                sum +
                (z.zone_index === zoneIndex
                  ? z.cells.filter((c) => c.row_index !== rowIndex).length
                  : z.cells.length),
              0,
            ),
          },
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  const addRow = useCallback(
    (zoneIndex: number, afterRowIndex: number) => {
      if (!grid) return
      const zone = grid.zones.find((z) => z.zone_index === zoneIndex)
      if (!zone) return
      pushUndo(grid.zones)

      const newRowIndex = afterRowIndex + 1

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((z) => {
            if (z.zone_index !== zoneIndex) return z
            // Shift existing rows
            const shiftedRows = z.rows.map((r) =>
              r.index > afterRowIndex ? { ...r, index: r.index + 1 } : r,
            )
            // Insert new row
            const refRow = z.rows.find((r) => r.index === afterRowIndex) || z.rows[z.rows.length - 1]
            const newRow = {
              index: newRowIndex,
              y_min_px: refRow.y_max_px,
              y_max_px: refRow.y_max_px + (refRow.y_max_px - refRow.y_min_px),
              y_min_pct: refRow.y_max_pct,
              y_max_pct: Math.min(100, refRow.y_max_pct + (refRow.y_max_pct - refRow.y_min_pct)),
              is_header: false,
              is_footer: false,
            }
            const allRows = [...shiftedRows, newRow].sort((a, b) => a.index - b.index)

            // Shift existing cells
            const shiftedCells = z.cells.map((c) => {
              if (c.row_index > afterRowIndex) {
                const newRI = c.row_index + 1
                return {
                  ...c,
                  row_index: newRI,
                  cell_id: `Z${zoneIndex}_R${String(newRI).padStart(2, '0')}_C${c.col_index}`,
                }
              }
              return c
            })
            // Create empty cells for each column
            const newCells = z.columns.map((col) => ({
              cell_id: `Z${zoneIndex}_R${String(newRowIndex).padStart(2, '0')}_C${col.index}`,
              zone_index: zoneIndex,
              row_index: newRowIndex,
              col_index: col.index,
              col_type: col.label,
              text: '',
              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 { ...z, rows: allRows, cells: [...shiftedCells, ...newCells] }
          }),
          summary: {
            ...prev.summary,
            total_rows: prev.summary.total_rows + 1,
            total_cells: prev.summary.total_cells + (zone?.columns.length ?? 0),
          },
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  // ------------------------------------------------------------------
  // Layout divider editing (image overlay)
  // ------------------------------------------------------------------

  /** Capture current state for undo — call once at drag start. */
  const commitUndoPoint = useCallback(() => {
    if (!grid) return
    pushUndo(grid.zones)
  }, [grid, pushUndo])

  /** Move a column boundary. boundaryIndex 0 = left edge of col 0, etc. */
  const updateColumnDivider = useCallback(
    (zoneIndex: number, boundaryIndex: number, newXPct: number) => {
      if (!grid) return
      setGrid((prev) => {
        if (!prev) return prev
        const imgW = prev.image_width || 1
        const newPx = Math.round((newXPct / 100) * imgW)
        return {
          ...prev,
          zones: prev.zones.map((z) => {
            if (z.zone_index !== zoneIndex) return z
            return {
              ...z,
              columns: z.columns.map((col) => {
                // Right edge of the column before this boundary
                if (col.index === boundaryIndex - 1) {
                  return { ...col, x_max_pct: newXPct, x_max_px: newPx }
                }
                // Left edge of the column at this boundary
                if (col.index === boundaryIndex) {
                  return { ...col, x_min_pct: newXPct, x_min_px: newPx }
                }
                return col
              }),
            }
          }),
        }
      })
      setDirty(true)
    },
    [grid, setGrid, setDirty],
  )

  /** Update horizontal layout guidelines (margins, header, footer). */
  const updateLayoutHorizontals = useCallback(
    (horizontals: LayoutDividers['horizontals']) => {
      if (!grid) return
      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          layout_dividers: {
            ...(prev.layout_dividers || { horizontals: {} }),
            horizontals,
          },
        }
      })
      setDirty(true)
    },
    [grid, setGrid, setDirty],
  )

  /** Split a column at a given x percentage, creating a new column. */
  const splitColumnAt = useCallback(
    (zoneIndex: number, xPct: number) => {
      if (!grid) return
      const zone = grid.zones.find((z) => z.zone_index === zoneIndex)
      if (!zone) return

      const sorted = [...zone.columns].sort((a, b) => a.index - b.index)
      const targetCol = sorted.find((c) => c.x_min_pct <= xPct && c.x_max_pct >= xPct)
      if (!targetCol) return

      pushUndo(grid.zones)
      const newColIndex = targetCol.index + 1
      const imgW = grid.image_width || 1

      setGrid((prev) => {
        if (!prev) return prev
        return {
          ...prev,
          zones: prev.zones.map((z) => {
            if (z.zone_index !== zoneIndex) return z
            const leftCol = {
              ...targetCol,
              x_max_pct: xPct,
              x_max_px: Math.round((xPct / 100) * imgW),
            }
            const rightCol = {
              index: newColIndex,
              label: `column_${newColIndex + 1}`,
              x_min_pct: xPct,
              x_max_pct: targetCol.x_max_pct,
              x_min_px: Math.round((xPct / 100) * imgW),
              x_max_px: targetCol.x_max_px,
              bold: false,
            }
            const updatedCols = z.columns.map((c) => {
              if (c.index === targetCol.index) return leftCol
              if (c.index > targetCol.index) return { ...c, index: c.index + 1, label: `column_${c.index + 2}` }
              return c
            })
            const allCols = [...updatedCols, rightCol].sort((a, b) => a.index - b.index)
            const shiftedCells = z.cells.map((c) => {
              if (c.col_index > targetCol.index) {
                const newCI = c.col_index + 1
                return {
                  ...c,
                  col_index: newCI,
                  cell_id: `Z${zoneIndex}_R${String(c.row_index).padStart(2, '0')}_C${newCI}`,
                }
              }
              return c
            })
            const newCells = z.rows.map((row) => ({
              cell_id: `Z${zoneIndex}_R${String(row.index).padStart(2, '0')}_C${newColIndex}`,
              zone_index: zoneIndex,
              row_index: row.index,
              col_index: newColIndex,
              col_type: `column_${newColIndex + 1}`,
              text: '',
              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 { ...z, columns: allCols, cells: [...shiftedCells, ...newCells] }
          }),
          summary: {
            ...prev.summary,
            total_columns: prev.summary.total_columns + 1,
            total_cells: prev.summary.total_cells + (zone.rows.length),
          },
        }
      })
      setDirty(true)
    },
    [grid, pushUndo, setGrid, setDirty],
  )

  return {
    deleteColumn,
    addColumn,
    deleteRow,
    addRow,
    commitUndoPoint,
    updateColumnDivider,
    updateLayoutHorizontals,
    splitColumnAt,
  }
}
