import React, { useState, useEffect, useCallback, useRef } from 'react';
import { 
  Undo2, Redo2, Trash2, Columns, 
  Plus, Shrink, Expand, Heading, MousePointer2, Combine, Type, Palette,
  Scissors, Merge as MergeIcon, Maximize, ChevronRight, Replace, Download, Wand2, Sigma
} from 'lucide-react';
import { cn } from '../lib/utils';

interface CellModel {
  id: string;
  value: string;
  rowSpan: number;
  colSpan: number;
  hidden: boolean;
  fontWeight?: string;
  backgroundColor?: string;
}

const INIT_ROWS = 6;
const INIT_COLS = 5;
const genId = () => `c_${Math.random().toString(36).slice(2, 9)}`;

const SYMBOL_PACKS = {
  Financial: ['$', '€', '£', '¥', '₹', '₽', '₩', '₺', '¢', '%', '‰', '↑', '↓', '±'],
  Scientific: ['θ', 'π', 'σ', 'μ', 'Δ', 'Ω', 'α', 'β', 'γ', 'λ', '∞', '≈', '≠', '≤', '≥', '√', '∫', '∑', '°', '±'],
  Math: ['+', '-', '×', '÷', '=', '≠', '<', '>', '≤', '≥', '±', '∑', '∏', '∫', '∞', '∴', '∵', '≈', '≅', '≡'],
  Arrows: ['←', '↑', '→', '↓', '↔', '↕', '↖', '↗', '↘', '↙', '↩', '↪'],
};

export function computeClassifications(grid: CellModel[][]): string[][] {
    const R = grid.length;
    const C = grid[0].length;
    let roles: string[][] = Array(R).fill(null).map(() => Array(C).fill('DataCell'));
    
    if (C === 1) {
        for (let r=0; r<R; r++) roles[r][0] = r === 0 ? 'TerminalHeader' : 'DataCell';
        return roles;
    }
    
    let topHeadersEnd = 1;
    let changed = true;
    while (changed && topHeadersEnd < R) {
        changed = false;
        for (let r = 0; r < topHeadersEnd; r++) {
            for (let c = 0; c < C; c++) {
                if (!grid[r][c].hidden) {
                    const reach = r + grid[r][c].rowSpan;
                    if (reach > topHeadersEnd) {
                        topHeadersEnd = reach;
                        changed = true;
                    }
                }
            }
        }
        if (topHeadersEnd < R && !changed) {
            let hasData = false;
            for (let c = 0; c < C; c++) {
                if (!grid[topHeadersEnd][c].hidden && grid[topHeadersEnd][c].value.trim() !== '') {
                    hasData = true;
                }
            }
            if (!hasData) {
                topHeadersEnd++;
                changed = true;
            }
        }
        if (topHeadersEnd < R && !changed) {
            let r = topHeadersEnd - 1;
            let hasColSpan = false;
            for (let c = 0; c < C; c++) {
                if (!grid[r][c].hidden && grid[r][c].colSpan > 1 && grid[r][c].value.trim() !== '') {
                    if (grid[r][c].colSpan < C) {
                        hasColSpan = true;
                    }
                }
            }
            if (hasColSpan) {
                topHeadersEnd++;
                changed = true;
            }
        }
    }

    for (let r = 0; r < R; r++) {
        let numVisibleInRow = 0;
        let lastVisibleCell = null;
        let lastVisibleC = -1;
        for (let c = 0; c < C; c++) {
            if (!grid[r][c].hidden) {
                numVisibleInRow++;
                lastVisibleCell = grid[r][c];
                lastVisibleC = c;
            }
        }

        for (let c = 0; c < C; c++) {
            const cell = grid[r][c];
            if (cell.hidden) continue;
            const rs = cell.rowSpan;
            const cs = cell.colSpan;
            
            if (r === 0 && c === 0 && (cell.value.trim() === '' || (rs > 1 && cs > 1))) {
                roles[r][c] = 'StubHeader'; continue;
            }
            if (cs === C && cell.value.trim() === '') {
                roles[r][c] = 'Padding'; continue;
            }
            let isRowEmptyAnd1x1 = true;
            for(let checkC = 0; checkC < C; checkC++){
                if(!grid[r][checkC].hidden && (grid[r][checkC].value.trim() !== '' || grid[r][checkC].rowSpan > 1 || grid[r][checkC].colSpan > 1)){
                    isRowEmptyAnd1x1 = false; break;
                }
            }
            if (isRowEmptyAnd1x1) {
                roles[r][c] = 'Padding'; continue;
            }
            if (r < topHeadersEnd) {
                if (cs > 1) roles[r][c] = 'SuperHeader';
                else if (cs === 1 && rs === 1) roles[r][c] = 'TerminalHeader';
                else roles[r][c] = 'SuperHeader';
                continue;
            }
            if (r === R - 1 && c === 0 && cs > 1) {
                roles[r][c] = 'FooterSpanner'; continue;
            }
            if (r > 0 && cs === C && C > 1) {
                roles[r][c] = 'StrictProjectedHeader'; continue;
            }
            if (r > 0 && (numVisibleInRow === 1 || numVisibleInRow === 2) && lastVisibleCell && lastVisibleCell.colSpan >= C - 1) {
                if (lastVisibleC === c) { roles[r][c] = 'LooseProjectedHeader'; continue; }
            }
            if ((c === 0 || (c === 1 && grid[r][0].hidden)) && rs > 1) {
                roles[r][c] = 'SuperRowHeader'; continue;
            }
            if (c === 0 && rs === 1 && r >= topHeadersEnd) {
                roles[r][c] = 'StandardRowHeader'; continue;
            }
            let col0Empty = true;
            for(let scanR=0; scanR<R; scanR++) {
                if (!grid[scanR][0].hidden && grid[scanR][0].value.trim() !== '') { col0Empty = false; break; }
            }
            if (col0Empty && c === 1 && rs === 1 && r >= topHeadersEnd) {
                roles[r][c] = 'StandardRowHeader'; continue;
            }
            roles[r][c] = 'DataCell';
        }
    }
    return roles;
}

export default function TableEditor() {
  const [grid, setGrid] = useState<CellModel[][]>([]);
  const [past, setPast] = useState<CellModel[][][]>([]);
  const [future, setFuture] = useState<CellModel[][][]>([]);
  
  // UI States
  const [spanMode, setSpanMode] = useState(false);
  const [showFind, setShowFind] = useState(false);
  const [showHeuristics, setShowHeuristics] = useState(false);
  const [showSymbols, setShowSymbols] = useState(false);
  const [categoryColors, setCategoryColors] = useState({
    StubHeader: '#f1f5f9', SuperHeader: '#dbeafe', TerminalHeader: '#bfdbfe',
    SuperRowHeader: '#fef3c7', StandardRowHeader: '#fde68a', Padding: '#f3f4f6',
    FooterSpanner: '#d1fae5', StrictProjectedHeader: '#e0e7ff', LooseProjectedHeader: '#c7d2fe',
    DataCell: '#ffffff'
  });
  const [findText, setFindText] = useState('');
  const [replaceText, setReplaceText] = useState('');
  const [isCompact, setIsCompact] = useState(false);
  const [hasHeader, setHasHeader] = useState(true);
  const [hasStripes, setHasStripes] = useState(false);
  const [focusedCell, setFocusedCell] = useState<{r: number, c: number} | null>(null);

  // Drag States for Spanning/Reordering
  const [spanDrag, setSpanDrag] = useState<{sr: number, sc: number, er: number, ec: number} | null>(null);

  // Drag States for Splitting Nodes
  const [nodeMode, setNodeMode] = useState<'split'|'merge'>('split');
  const [mergeFullSpan, setMergeFullSpan] = useState(false);
  const [nodeDrag, setNodeDrag] = useState<any>(null);
  const [dragPreview, setDragPreview] = useState<any>(null);

  useEffect(() => {
    const initial: CellModel[][] = [];
    for (let r = 0; r < INIT_ROWS; r++) {
      const row: CellModel[] = [];
      for (let c = 0; c < INIT_COLS; c++) {
        row.push({
          id: genId(), value: r === 0 ? `Header ${c + 1}` : `Data ${r},${c + 1}`,
          rowSpan: 1, colSpan: 1, hidden: false
        });
      }
      initial.push(row);
    }
    setGrid(initial);
  }, []);

  const cloneGrid = (g: CellModel[][]) => g.map(row => row.map(cell => ({...cell})));

  const updateGrid = useCallback((newGrid: CellModel[][]) => {
    setPast(oldPast => [...oldPast, cloneGrid(grid)].slice(-50));
    setFuture([]);
    setGrid(newGrid);
  }, [grid]);

  const handleUndo = useCallback(() => {
    setPast(oldPast => {
      if (!oldPast.length) return oldPast;
      const prev = oldPast[oldPast.length - 1];
      setGrid(currentGrid => {
        setFuture(oldFuture => [cloneGrid(currentGrid), ...oldFuture]);
        return cloneGrid(prev);
      });
      return oldPast.slice(0, -1);
    });
  }, []);

  const handleRedo = useCallback(() => {
    setFuture(oldFuture => {
      if (!oldFuture.length) return oldFuture;
      const next = oldFuture[0];
      setGrid(currentGrid => {
        setPast(oldPast => [...oldPast, cloneGrid(currentGrid)]);
        return cloneGrid(next);
      });
      return oldFuture.slice(1);
    });
  }, []);

  const updateCellValue = (r: number, c: number, val: string) => {
    if (grid[r][c].value === val) return;
    const newGrid = cloneGrid(grid);
    newGrid[r][c].value = val;
    updateGrid(newGrid);
  };

  const getVisibleCell = (g: CellModel[][], targetR: number, targetC: number) => {
    for(let r=targetR; r>=0; r--) {
      for(let c=targetC; c>=0; c--) {
        const cell = g[r][c];
        if(!cell.hidden && r + cell.rowSpan > targetR && c + cell.colSpan > targetC) {
          return cell;
        }
      }
    }
    return g[targetR][targetC];
  };

  const trimEmpty = () => {
    let g = cloneGrid(grid);
    for (let r = g.length - 1; r >= 0; r--) {
      let isEmpty = true;
      for (let c = 0; c < g[r].length; c++) {
        if (getVisibleCell(g, r, c).value.trim()) { isEmpty = false; break; }
      }
      if (isEmpty) {
        for (let r_i = 0; r_i < r; r_i++) {
          for (let c = 0; c < g[0].length; c++) {
            if (!g[r_i][c].hidden && g[r_i][c].rowSpan > r - r_i) g[r_i][c].rowSpan--;
          }
        }
        g.splice(r, 1);
      }
    }
    if (g.length > 0) {
      for (let c = g[0].length - 1; c >= 0; c--) {
        let isEmpty = true;
        for (let r = 0; r < g.length; r++) {
          if (getVisibleCell(g, r, c).value.trim()) { isEmpty = false; break; }
        }
        if (isEmpty) {
          for (let r = 0; r < g.length; r++) {
            for (let c_i = 0; c_i < c; c_i++) {
              if (!g[r][c_i].hidden && g[r][c_i].colSpan > c - c_i) g[r][c_i].colSpan--;
            }
            g[r].splice(c, 1);
          }
        }
      }
    }
    if (g.length && g[0].length) updateGrid(g);
  };

  const addRow = (index: number) => {
    const newGrid = cloneGrid(grid);
    insertRow(newGrid, index);
    updateGrid(newGrid);
  };

  const addCol = (index: number) => {
    const newGrid = cloneGrid(grid);
    insertCol(newGrid, index);
    updateGrid(newGrid);
  };

  const isDraggingSelection = useRef(false);

  const handleCellMouseDown = (e: React.MouseEvent, r: number, c: number) => {
    if (!spanMode) return;
    e.preventDefault();
    isDraggingSelection.current = true;
    setSpanDrag({ sr: r, sc: c, er: r, ec: c });
  };
  
  const handleCellMouseEnter = (r: number, c: number) => {
    if (spanMode && isDraggingSelection.current && spanDrag) {
      setSpanDrag({ ...spanDrag, er: r, ec: c });
    }
  };

  useEffect(() => {
    const handleMouseUp = () => {
      isDraggingSelection.current = false;
    };
    window.addEventListener('mouseup', handleMouseUp);
    return () => window.removeEventListener('mouseup', handleMouseUp);
  }, []);

  const executeMerge = (r1: number, c1: number, r2: number, c2: number) => {
    let minR = Math.min(r1, r2), maxR = Math.max(r1, r2);
    let minC = Math.min(c1, c2), maxC = Math.max(c1, c2);
    
    const newGrid = cloneGrid(grid);

    // Expand bounding box to encompass all intersecting spanned cells
    let expanded = true;
    while(expanded) {
      expanded = false;
      for (let r = 0; r < newGrid.length; r++) {
        for (let c = 0; c < newGrid[0].length; c++) {
          const cell = newGrid[r][c];
          if (!cell.hidden) {
            const maxCellR = r + cell.rowSpan - 1;
            const maxCellC = c + cell.colSpan - 1;
            if (r <= maxR && maxCellR >= minR && c <= maxC && maxCellC >= minC) {
              if (r < minR) { minR = r; expanded = true; }
              if (maxCellR > maxR) { maxR = maxCellR; expanded = true; }
              if (c < minC) { minC = c; expanded = true; }
              if (maxCellC > maxC) { maxC = maxCellC; expanded = true; }
            }
          }
        }
      }
    }

    if (minR === maxR && minC === maxC) return;
    
    const combinedVals: string[] = [];
    for (let r = minR; r <= maxR; r++) {
      for (let c = minC; c <= maxC; c++) {
        const cell = newGrid[r][c];
        if (!cell.hidden && cell.value.trim()) combinedVals.push(cell.value);
        cell.hidden = true; cell.rowSpan = 1; cell.colSpan = 1;
      }
    }
    const anchor = newGrid[minR][minC];
    anchor.hidden = false;
    anchor.rowSpan = maxR - minR + 1;
    anchor.colSpan = maxC - minC + 1;
    anchor.value = combinedVals.join(' ');
    updateGrid(newGrid);
  };

  const handleMergeFull = (r: number, c: number, edge: 'top'|'bottom'|'left'|'right') => {
    let tR = r; let tC = c;
    if (edge === 'right') tC = grid[0].length - 1;
    if (edge === 'left') tC = 0;
    if (edge === 'bottom') tR = grid.length - 1;
    if (edge === 'top') tR = 0;
    if (tR !== r || tC !== c) {
      executeMerge(r, c, tR, tC);
    }
  };

  // --- SPLIT & DROP LOGIC ---
  const syncGridFromDOM = () => {
    const newGrid = cloneGrid(grid);
    document.querySelectorAll('[data-cell-r]').forEach((el) => {
      const r = parseInt(el.getAttribute('data-cell-r')!);
      const c = parseInt(el.getAttribute('data-cell-c')!);
      const val = (el as HTMLElement).innerText;
      if (newGrid[r] && newGrid[r][c] && newGrid[r][c].value !== val) {
        newGrid[r][c].value = val;
      }
    });
    return newGrid;
  };

  const insertRow = (g: CellModel[][], insertIndex: number, cValues: Record<number, string> | null = null) => {
    const isSplit = cValues !== null;
    const numCols = g[0].length;
    const newRow = Array.from({length: numCols}).map((_, i) => ({
      id: genId(), value: cValues ? (cValues[i] || '') : '', rowSpan: 1, colSpan: 1, hidden: false
    }));
    g.splice(insertIndex, 0, newRow);

    for (let r = 0; r < insertIndex; r++) {
      for (let c = 0; c < numCols; c++) {
        const cell = g[r][c];
        const spanCondition = isSplit ? 
          (r + cell.rowSpan >= insertIndex) : 
          (r + cell.rowSpan > insertIndex);

        if (!cell.hidden && spanCondition) {
          let explicitlySplit = false;
          for (let ci = c; ci < c + cell.colSpan; ci++) {
            if (cValues && cValues[ci] !== undefined) explicitlySplit = true;
          }
          if (!explicitlySplit) {
            cell.rowSpan++;
            for (let ci = c; ci < c + cell.colSpan; ci++) {
              g[insertIndex][ci].hidden = true;
            }
          } else if (r + cell.rowSpan > insertIndex) {
             const oldRowSpan = cell.rowSpan;
             const topSpan = insertIndex - r;
             const bottomSpan = oldRowSpan - topSpan + 1;
             cell.rowSpan = topSpan;
             
             const splitCell = g[insertIndex][c];
             splitCell.hidden = false;
             splitCell.rowSpan = bottomSpan;
             splitCell.colSpan = cell.colSpan;
             for (let ci = c + 1; ci < c + cell.colSpan; ci++) {
               g[insertIndex][ci].hidden = true;
             }
          } else if (r + cell.rowSpan === insertIndex) {
             const splitCell = g[insertIndex][c];
             splitCell.colSpan = cell.colSpan;
             for (let ci = c + 1; ci < c + cell.colSpan; ci++) {
               g[insertIndex][ci].hidden = true;
             }
          }
        }
      }
    }
  };

  const insertCol = (g: CellModel[][], insertIndex: number, rValues: Record<number, string> | null = null) => {
    const isSplit = rValues !== null;
    const numRows = g.length;
    for (let ri = 0; ri < numRows; ri++) {
      g[ri].splice(insertIndex, 0, {
        id: genId(), value: rValues ? (rValues[ri] || '') : '', rowSpan: 1, colSpan: 1, hidden: false
      });
    }

    for (let r = 0; r < numRows; r++) {
      for (let c = 0; c < insertIndex; c++) {
        const cell = g[r][c];
        const spanCondition = isSplit ? 
          (c + cell.colSpan >= insertIndex) : 
          (c + cell.colSpan > insertIndex);

        if (!cell.hidden && spanCondition) {
          let explicitlySplit = false;
          for (let ri = r; ri < r + cell.rowSpan; ri++) {
            if (rValues && rValues[ri] !== undefined) explicitlySplit = true;
          }
          if (!explicitlySplit) {
            cell.colSpan++;
            for (let ri = r; ri < r + cell.rowSpan; ri++) {
              g[ri][insertIndex].hidden = true;
            }
          } else if (c + cell.colSpan > insertIndex) {
             const oldColSpan = cell.colSpan;
             const leftSpan = insertIndex - c;
             const rightSpan = oldColSpan - leftSpan + 1;
             cell.colSpan = leftSpan;

             const splitCell = g[r][insertIndex];
             splitCell.hidden = false;
             splitCell.colSpan = rightSpan;
             splitCell.rowSpan = cell.rowSpan;
             for (let ri = r + 1; ri < r + cell.rowSpan; ri++) {
               g[ri][insertIndex].hidden = true;
             }
          } else if (c + cell.colSpan === insertIndex) {
             const splitCell = g[r][insertIndex];
             splitCell.rowSpan = cell.rowSpan;
             for (let ri = r + 1; ri < r + cell.rowSpan; ri++) {
               g[ri][insertIndex].hidden = true;
             }
          }
        }
      }
    }
  };

  const handleNodeCrossSplit = (dropR: number, dropC: number) => {
    if (!nodeDrag) return;
    const isVerticalLine = nodeDrag.edge === 'top' || nodeDrag.edge === 'bottom';
    const minR = Math.min(nodeDrag.r, dropR);
    const maxR = Math.max(nodeDrag.r, dropR);
    const minC = Math.min(nodeDrag.c, dropC);
    const maxC = Math.max(nodeDrag.c, dropC);

    const currentGrid = syncGridFromDOM();
    if (isVerticalLine) {
      const rValues: Record<number, string> = {};
      for(let ri = minR; ri <= maxR; ri++) rValues[ri] = '';
      const cTarget = nodeDrag.c;
      insertCol(currentGrid, cTarget + 1, rValues);
    } else {
      const cValues: Record<number, string> = {};
      for(let ci = minC; ci <= maxC; ci++) cValues[ci] = '';
      const rTarget = nodeDrag.r;
      insertRow(currentGrid, rTarget + 1, cValues);
    }
    updateGrid(currentGrid);
    setNodeDrag(null);
    setDragPreview(null);
  };

  const splitCellAtEdge = (r: number, c: number, edge: 'top'|'bottom'|'left'|'right', text: string = '') => {
    const sel = window.getSelection();
    if (sel && text) sel.deleteFromDocument(); // deletes natively dragged text
    const currentGrid = syncGridFromDOM(); // picks up DOM after deletion
    const cell = currentGrid[r][c];

    if (edge === 'bottom') {
      if (cell.rowSpan > 1) {
        cell.rowSpan -= 1;
        const newCell = currentGrid[r + cell.rowSpan][c];
        newCell.hidden = false; newCell.rowSpan = 1; newCell.colSpan = cell.colSpan;
        if (text) newCell.value = text;
      } else {
        insertRow(currentGrid, r + 1, { [c]: text });
      }
    } else if (edge === 'top') {
      if (cell.rowSpan > 1) {
        const oldSpan = cell.rowSpan; cell.rowSpan = 1;
        const newCell = currentGrid[r+1][c];
        newCell.hidden = false; newCell.rowSpan = oldSpan - 1; newCell.colSpan = cell.colSpan;
        newCell.value = cell.value; cell.value = text;
      } else {
        insertRow(currentGrid, r + 1, { [c]: cell.value });
        currentGrid[r][c].value = text;
      }
    } else if (edge === 'right') {
      if (cell.colSpan > 1) {
        cell.colSpan -= 1;
        const newCell = currentGrid[r][c + cell.colSpan];
        newCell.hidden = false; newCell.colSpan = 1; newCell.rowSpan = cell.rowSpan;
        if (text) newCell.value = text;
      } else {
        insertCol(currentGrid, c + 1, { [r]: text });
      }
    } else if (edge === 'left') {
      if (cell.colSpan > 1) {
        const oldSpan = cell.colSpan; cell.colSpan = 1;
        const newCell = currentGrid[r][c+1];
        newCell.hidden = false; newCell.colSpan = oldSpan - 1; newCell.rowSpan = cell.rowSpan;
        newCell.value = cell.value; cell.value = text;
      } else {
        insertCol(currentGrid, c + 1, { [r]: cell.value });
        currentGrid[r][c].value = text;
      }
    }
    updateGrid(currentGrid);
  };

  const executeReplaceAll = () => {
    if (!findText) return;
    const g = cloneGrid(grid);
    let minR = 0, maxR = g.length - 1;
    let minC = 0, maxC = g[0].length - 1;
    if (spanMode && spanDrag) {
      minR = Math.min(spanDrag.sr, spanDrag.er); maxR = Math.max(spanDrag.sr, spanDrag.er);
      minC = Math.min(spanDrag.sc, spanDrag.ec); maxC = Math.max(spanDrag.sc, spanDrag.ec);
    }
    for (let r = minR; r <= maxR; r++) {
      for (let c = minC; c <= maxC; c++) {
        if (!g[r][c].hidden) {
          g[r][c].value = g[r][c].value.split(findText).join(replaceText);
        }
      }
    }
    updateGrid(g);
  };

  const toggleBold = () => {
    const g = cloneGrid(grid);
    if (spanMode && spanDrag) {
      const minR = Math.min(spanDrag.sr, spanDrag.er), maxR = Math.max(spanDrag.sr, spanDrag.er);
      const minC = Math.min(spanDrag.sc, spanDrag.ec), maxC = Math.max(spanDrag.sc, spanDrag.ec);
      let allBold = true;
      for (let r = minR; r <= maxR; r++) {
        for (let c = minC; c <= maxC; c++) {
          if (!g[r][c].hidden && g[r][c].fontWeight !== 'bold') allBold = false;
        }
      }
      for (let r = minR; r <= maxR; r++) {
        for (let c = minC; c <= maxC; c++) {
          if (!g[r][c].hidden) g[r][c].fontWeight = allBold ? '' : 'bold';
        }
      }
    } else if (focusedCell) {
      g[focusedCell.r][focusedCell.c].fontWeight = g[focusedCell.r][focusedCell.c].fontWeight ? '' : 'bold';
    } else return;
    updateGrid(g);
  };

  const cycleColor = () => {
    const colors = ['', 'bg-blue-100', 'bg-emerald-100', 'bg-amber-100', 'bg-rose-100', 'bg-brand-500 text-white border-brand-600'];
    const g = cloneGrid(grid);
    if (spanMode && spanDrag) {
      const minR = Math.min(spanDrag.sr, spanDrag.er), minC = Math.min(spanDrag.sc, spanDrag.ec);
      const cell = g[minR][minC];
      const currentIdx = colors.indexOf(cell.backgroundColor || '');
      const nextColor = colors[(currentIdx + 1) % colors.length];
      const maxR = Math.max(spanDrag.sr, spanDrag.er), maxC = Math.max(spanDrag.sc, spanDrag.ec);
      for (let r = minR; r <= maxR; r++) {
        for (let c = minC; c <= maxC; c++) {
          if (!g[r][c].hidden) g[r][c].backgroundColor = nextColor;
        }
      }
    } else if (focusedCell) {
      const cell = g[focusedCell.r][focusedCell.c];
      const currentIdx = colors.indexOf(cell.backgroundColor || '');
      cell.backgroundColor = colors[(currentIdx + 1) % colors.length];
    } else return;
    updateGrid(g);
  };

  const exportCSV = () => {
    const R = grid.length;
    const C = grid[0].length;
    let csvContent = "";
    for (let r = 0; r < R; r++) {
        let rowCells = [];
        for (let c = 0; c < C; c++) {
            if (grid[r][c].hidden) {
                rowCells.push("");
            } else {
                let cellVal = grid[r][c].value.replace(/"/g, '""');
                rowCells.push(`"${cellVal}"`);
            }
        }
        csvContent += rowCells.join(",") + "\n";
    }
    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', 'table.csv');
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  const roles = React.useMemo(() => showHeuristics ? computeClassifications(grid) : null, [showHeuristics, grid]);

  if (!grid.length) return null;

  return (
    <div className="h-full flex flex-col items-center relative">
      <div className="mt-8 mb-4 px-4 py-2 bg-white rounded-xl shadow-lg border border-slate-200 flex items-center gap-1.5 ring-1 ring-black/5 z-50">
        <ToolbarButton icon={Undo2} onClick={handleUndo} disabled={!past.length} title="Undo" />
        <ToolbarButton icon={Redo2} onClick={handleRedo} disabled={!future.length} title="Redo" />
        <div className="w-px h-6 bg-slate-200 mx-2" />
        <ToolbarButton icon={spanMode ? Combine : MousePointer2} onClick={() => setSpanMode(!spanMode)} active={spanMode} title="Select Cells Mode" />
        {spanMode && <ToolbarButton icon={Combine} onClick={() => { if (spanDrag) executeMerge(spanDrag.sr, spanDrag.sc, spanDrag.er, spanDrag.ec); }} title="Merge Selected" />}
        <ToolbarButton icon={Scissors} onClick={() => { setNodeMode('split'); setSpanMode(false); }} active={!spanMode && nodeMode === 'split'} title="Node Mode: Split" />
        <ToolbarButton icon={MergeIcon} onClick={() => { setNodeMode('merge'); setSpanMode(false); }} active={!spanMode && nodeMode === 'merge'} title="Node Mode: Merge" />
        {nodeMode === 'merge' && !spanMode && (
          <ToolbarButton icon={Maximize} onClick={() => setMergeFullSpan(!mergeFullSpan)} active={mergeFullSpan} title="Merge to Full Row/Col" />
        )}
        <ToolbarButton icon={Trash2} onClick={trimEmpty} title="Trim Empty Rows/Cols" />
        <div className="w-px h-6 bg-slate-200 mx-2" />
        <ToolbarButton icon={isCompact ? Shrink : Expand} onClick={() => setIsCompact(!isCompact)} active={isCompact} title="Compact Text" />
        <ToolbarButton icon={Heading} onClick={() => setHasHeader(!hasHeader)} active={hasHeader} title="Header Styling" />
        <ToolbarButton icon={Columns} onClick={() => setHasStripes(!hasStripes)} active={hasStripes} title="Striped Rows" />
        <div className="w-px h-6 bg-slate-200 mx-2" />
        <ToolbarButton icon={Type} onClick={toggleBold} active={focusedCell && grid[focusedCell.r]?.[focusedCell.c]?.fontWeight === 'bold'} title="Bold" />
        <ToolbarButton icon={Palette} onClick={cycleColor} active={focusedCell && !!grid[focusedCell.r]?.[focusedCell.c]?.backgroundColor} title="Colorize Cell" />
        <ToolbarButton icon={Replace} onClick={() => setShowFind(!showFind)} active={showFind} title="Find & Replace" />
        <ToolbarButton icon={Sigma} onClick={() => setShowSymbols(!showSymbols)} active={showSymbols} title="Symbols" />
        <ToolbarButton icon={Wand2} onClick={() => setShowHeuristics(!showHeuristics)} active={showHeuristics} title="Classify Cells (Heuristics X-Ray)" />
        <div className="w-px h-6 bg-slate-200 mx-2" />
        <ToolbarButton icon={Download} onClick={exportCSV} title="Export CSV" />
      </div>
      
      {showFind && (
        <div className="absolute top-[88px] left-1/2 -translate-x-1/2 z-50 bg-white shadow-xl border border-slate-200 rounded-lg p-2 flex items-center gap-2">
          <input 
            type="text" 
            placeholder="Find..." 
            value={findText}
            onChange={e => setFindText(e.target.value)}
            className="px-2 py-1 border border-slate-200 rounded text-sm w-32 focus:outline-brand-500"
          />
          <input 
            type="text" 
            placeholder="Replace..." 
            value={replaceText}
            onChange={e => setReplaceText(e.target.value)}
            className="px-2 py-1 border border-slate-200 rounded text-sm w-32 focus:outline-brand-500"
          />
          <button 
            onClick={executeReplaceAll}
            disabled={!findText}
            className="px-3 py-1 bg-brand-500 text-white rounded text-sm hover:bg-brand-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
          >
            Replace All
          </button>
        </div>
      )}

      {showHeuristics && (
        <div className="absolute top-[88px] left-1/2 -translate-x-1/2 z-50 bg-white shadow-xl border border-slate-200 rounded-lg p-4 flex flex-col gap-3 w-80">
          <div className="flex items-center justify-between border-b border-slate-100 pb-2 mb-1">
            <h3 className="font-medium text-sm text-slate-800">Heuristics Coloring</h3>
          </div>
          <div className="grid grid-cols-1 gap-x-4 gap-y-2 max-h-64 overflow-y-auto">
            {Object.entries(categoryColors).map(([cat, color]) => (
              <div key={cat} className="flex items-center justify-between gap-3 group">
                <span className="text-xs text-slate-600 font-medium truncate group-hover:text-slate-900">{cat}</span>
                <input 
                  type="color" 
                  value={color}
                  onChange={(e) => setCategoryColors(prev => ({...prev, [cat]: e.target.value}))}
                  className="w-6 h-6 p-0 border-0 rounded cursor-pointer shrink-0 shadow-sm"
                />
              </div>
            ))}
          </div>
        </div>
      )}

      {showSymbols && (
        <div className="absolute top-[88px] left-1/2 -translate-x-1/2 z-50 bg-white shadow-xl border border-slate-200 rounded-lg p-4 flex flex-col gap-4 w-[480px]">
          <div className="flex items-center justify-between border-b border-slate-100 pb-2">
            <h3 className="font-medium text-sm text-slate-800">Quick Symbols</h3>
            <span className="text-xs text-slate-400">Click to copy</span>
          </div>
          <div className="flex flex-col gap-4 max-h-80 overflow-y-auto">
            {Object.entries(SYMBOL_PACKS).map(([packName, symbols]) => (
              <div key={packName} className="flex flex-col gap-1.5">
                <span className="text-xs font-semibold text-slate-500 uppercase tracking-wider">{packName}</span>
                <div className="flex flex-wrap gap-1.5">
                  {symbols.map(sym => (
                    <button 
                      key={sym}
                      onClick={() => navigator.clipboard.writeText(sym)}
                      className="w-8 h-8 flex items-center justify-center rounded bg-slate-50 border border-slate-200 hover:bg-brand-50 hover:border-brand-300 hover:text-brand-700 active:scale-95 transition-all text-sm font-medium text-slate-700"
                    >
                      {sym}
                    </button>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      <div className={cn("flex-1 w-full overflow-auto p-8 pt-0 flex justify-center pb-32", spanMode && "cursor-crosshair")}>
        <div className="relative inline-block w-full max-w-7xl mx-auto h-max flex justify-center">
          <table className={cn(
            "border-collapse bg-white shadow-md ring-1 ring-slate-300 rounded-sm transition-all duration-300 mx-auto", 
            isCompact ? "w-max" : "w-full table-fixed"
          )}>
            <thead>
              <tr>
                <th className="w-8 shrink-0 bg-transparent border-0" />
                {grid[0].map((_, c) => (
                  <th key={`colhead-${c}`}
                      className="relative group h-8 bg-slate-100 border-b border-r border-slate-300 select-none transition-colors"
                      onClick={() => { setSpanMode(true); setSpanDrag({ sr: 0, sc: c, er: grid.length - 1, ec: c }); }}>
                    <button onClick={() => addCol(c + 1)}
                            className="absolute -right-2 top-1/2 -translate-y-1/2 w-4 h-4 bg-brand-500 rounded-full text-white flex items-center justify-center opacity-0 group-hover:opacity-100 hover:scale-125 z-40 shadow-sm transition-all cursor-pointer shadow-brand-500/50">
                      <Plus size={10} strokeWidth={3} />
                    </button>
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {grid.map((row, r) => (
                <tr key={`row-${r}`}>
                  <th className="relative group w-8 bg-slate-100 border-b border-r border-slate-300 select-none transition-colors"
                      onClick={() => { setSpanMode(true); setSpanDrag({ sr: r, sc: 0, er: r, ec: grid[0].length - 1 }); }}>
                    <button onClick={() => addRow(r + 1)}
                            className="absolute left-1/2 -translate-x-1/2 -bottom-2 w-4 h-4 bg-brand-500 rounded-full text-white flex items-center justify-center opacity-0 group-hover:opacity-100 hover:scale-125 z-40 shadow-sm transition-all cursor-pointer shadow-brand-500/50">
                      <Plus size={10} strokeWidth={3} />
                    </button>
                  </th>
                  {row.map((cell, c) => {
                    if (cell.hidden) return null;
                    let isTargeted = false;
                    if (spanDrag && spanMode) {
                      const minR = Math.min(spanDrag.sr, spanDrag.er), maxR = Math.max(spanDrag.sr, spanDrag.er);
                      const minC = Math.min(spanDrag.sc, spanDrag.ec), maxC = Math.max(spanDrag.sc, spanDrag.ec);
                      if (r >= minR && r <= maxR && c >= minC && c <= maxC) isTargeted = true;
                    }
                    const isHeaderCell = hasHeader && r === 0;
                    const isStriped = hasStripes && (!hasHeader || r > 0) && r % 2 === 1;

                    return (
                      <td key={cell.id} rowSpan={cell.rowSpan} colSpan={cell.colSpan}
                          onMouseDown={(e) => handleCellMouseDown(e, r, c)}
                          onMouseEnter={() => handleCellMouseEnter(r, c)}
                          onDragOver={(e) => {
                            if (nodeDrag) {
                              e.preventDefault();
                              if (nodeDrag.mode === 'split') {
                                const isVerticalLine = nodeDrag.edge === 'top' || nodeDrag.edge === 'bottom';
                                setDragPreview({ 
                                  type: 'line', 
                                  isVertical: isVerticalLine, 
                                  minR: Math.min(nodeDrag.r, r), 
                                  maxR: Math.max(nodeDrag.r, r),
                                  minC: Math.min(nodeDrag.c, c),
                                  maxC: Math.max(nodeDrag.c, c),
                                  srcR: nodeDrag.r,
                                  srcC: nodeDrag.c
                                });
                              } else if (nodeDrag.mode === 'merge') {
                                let tR = r; let tC = c;
                                if (mergeFullSpan) {
                                  if (nodeDrag.edge === 'right') tC = grid[0].length - 1;
                                  if (nodeDrag.edge === 'left') tC = 0;
                                  if (nodeDrag.edge === 'bottom') tR = grid.length - 1;
                                  if (nodeDrag.edge === 'top') tR = 0;
                                  if (nodeDrag.edge === 'left' || nodeDrag.edge === 'right') tR = nodeDrag.r;
                                  if (nodeDrag.edge === 'top' || nodeDrag.edge === 'bottom') tC = nodeDrag.c;
                                }
                                setDragPreview({
                                  type: 'merge',
                                  minR: Math.min(nodeDrag.r, tR),
                                  maxR: Math.max(nodeDrag.r, tR),
                                  minC: Math.min(nodeDrag.c, tC),
                                  maxC: Math.max(nodeDrag.c, tC)
                                });
                              }
                            }
                          }}
                          onDragLeave={(e) => {
                             if (nodeDrag) setDragPreview(null);
                          }}
                          onDrop={(e) => {
                            if (nodeDrag) {
                              e.preventDefault();
                              e.stopPropagation();
                              if (nodeDrag.mode === 'split') {
                                handleNodeCrossSplit(r, c);
                              } else if (nodeDrag.mode === 'merge') {
                                let tR = r; let tC = c;
                                if (mergeFullSpan) {
                                  if (nodeDrag.edge === 'right') tC = grid[0].length - 1;
                                  if (nodeDrag.edge === 'left') tC = 0;
                                  if (nodeDrag.edge === 'bottom') tR = grid.length - 1;
                                  if (nodeDrag.edge === 'top') tR = 0;
                                  if (nodeDrag.edge === 'left' || nodeDrag.edge === 'right') tR = nodeDrag.r;
                                  if (nodeDrag.edge === 'top' || nodeDrag.edge === 'bottom') tC = nodeDrag.c;
                                }
                                executeMerge(nodeDrag.r, nodeDrag.c, tR, tC);
                              }
                              setNodeDrag(null);
                              setDragPreview(null);
                            }
                          }}
                          className={cn("relative group/td border border-slate-300 min-w-[80px] p-0 align-top transition-colors duration-100",
                            (roles && showHeuristics) ? "text-slate-900" :
                                [isHeaderCell ? "bg-slate-800 text-white font-medium shadow-sm border-slate-700" : "text-slate-700 bg-white",
                                 isStriped && !isHeaderCell && "bg-slate-50",
                                 cell.backgroundColor],
                            isTargeted && "bg-brand-100/80 ring-2 ring-inset ring-brand-500 shadow-inner z-10",
                            spanDrag && !isTargeted && "opacity-60",
                            cell.fontWeight === 'bold' && "font-bold"
                          )}
                          style={roles && showHeuristics ? { backgroundColor: categoryColors[roles[r][c] as keyof typeof categoryColors] } : undefined}>
                        
                        {!spanMode && (
                          <>
                            {['top', 'bottom', 'left', 'right'].map((edge: any) => (
                              <EdgeZone key={edge} r={r} c={c} edge={edge} 
                                nodeDrag={nodeDrag} setNodeDrag={setNodeDrag} 
                                dragPreview={dragPreview} setDragPreview={setDragPreview} 
                                splitCellAtEdge={splitCellAtEdge} 
                                nodeMode={nodeMode}
                                mergeFullSpan={mergeFullSpan}
                                handleMergeFull={handleMergeFull}
                              />
                            ))}
                            {dragPreview && dragPreview.type === 'line' && (
                              (dragPreview.isVertical && c === dragPreview.srcC && r >= dragPreview.minR && r <= dragPreview.maxR) ? (
                                <div className="absolute top-0 bottom-0 left-1/2 border-l-2 border-dashed border-brand-500 z-40 -translate-x-1/2 pointer-events-none" />
                              ) : (!dragPreview.isVertical && r === dragPreview.srcR && c >= dragPreview.minC && c <= dragPreview.maxC) ? (
                                <div className="absolute left-0 right-0 top-1/2 border-t-2 border-dashed border-brand-500 z-40 -translate-y-1/2 pointer-events-none" />
                              ) : null
                            )}
                            {dragPreview && dragPreview.type === 'merge' && (
                              (r >= dragPreview.minR && r <= dragPreview.maxR && c >= dragPreview.minC && c <= dragPreview.maxC) && (
                                <div className={cn("absolute inset-0 bg-amber-500/20 pointer-events-none z-40",
                                  r === dragPreview.minR && "border-t-2 border-amber-500",
                                  r === dragPreview.maxR && "border-b-2 border-amber-500",
                                  c === dragPreview.minC && "border-l-2 border-amber-500",
                                  c === dragPreview.maxC && "border-r-2 border-amber-500"
                                )} />
                              )
                            )}
                          </>
                        )}

                        <div
                          data-cell-r={r} data-cell-c={c}
                          contentEditable={!spanMode}
                          suppressContentEditableWarning
                          onFocus={() => setFocusedCell({r, c})}
                          onBlur={(e) => updateCellValue(r, c, e.currentTarget.innerText)}
                          className={cn("w-full h-full min-h-[44px] p-3 outline-none transition-shadow relative z-10",
                            !spanMode && "focus:ring-2 focus:ring-inset focus:ring-brand-500",
                            isCompact ? "whitespace-nowrap" : "whitespace-pre-wrap break-words"
                          )}
                        >
                          {cell.value}
                        </div>
                      </td>
                    );
                  })}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

const EdgeZone = ({ r, c, edge, nodeDrag, setNodeDrag, dragPreview, setDragPreview, splitCellAtEdge, nodeMode, mergeFullSpan, handleMergeFull }: any) => {
  const isPreview = dragPreview?.r === r && dragPreview?.c === c && dragPreview?.edge === edge;
  const isMerge = nodeMode === 'merge';

  return (
    <div
      className={cn("absolute z-30 flex items-center justify-center opacity-0 group-hover/td:opacity-100 transition-opacity",
        edge === 'top' && "left-4 right-4 top-0 h-4",
        edge === 'top' && (isMerge ? "translate-y-1" : "-translate-y-2"),
        edge === 'bottom' && "left-4 right-4 bottom-0 h-4",
        edge === 'bottom' && (isMerge ? "-translate-y-1" : "translate-y-2"),
        edge === 'left' && "top-4 bottom-4 left-0 w-4",
        edge === 'left' && (isMerge ? "translate-x-1" : "-translate-x-2"),
        edge === 'right' && "top-4 bottom-4 right-0 w-4",
        edge === 'right' && (isMerge ? "-translate-x-1" : "translate-x-2"),
      )}
      onDragEnter={(e) => {
        if (!nodeDrag) { e.preventDefault(); e.stopPropagation(); }
      }}
      onDragOver={(e) => { 
        if (!nodeDrag) { // Only preview EdgeZone when dragging text, not nodes
          e.preventDefault(); e.stopPropagation(); setDragPreview({r, c, edge}); 
        }
      }}
      onDragLeave={() => { if (!nodeDrag) setDragPreview(null); }}
      onDrop={(e) => {
        if (nodeDrag) return; // Nodes are dropped on td, not EdgeZone
        e.preventDefault(); e.stopPropagation(); setDragPreview(null);
        const text = e.dataTransfer.getData('text/plain');
        if (text) {
          splitCellAtEdge(r, c, edge, text);
        }
      }}
      onClick={(e) => { 
        e.stopPropagation(); 
        if (nodeMode === 'split') {
          splitCellAtEdge(r, c, edge); 
        } else if (nodeMode === 'merge' && mergeFullSpan) {
          handleMergeFull(r, c, edge);
        }
      }}
    >
      <div 
        draggable 
        onDragStart={(e) => { 
          e.dataTransfer.setData('node', 'true');
          const img = new Image();
          img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
          e.dataTransfer.setDragImage(img, 0, 0);
          setNodeDrag({r, c, edge, mode: nodeMode}); 
        }} 
        onDragEnd={() => setNodeDrag(null)}
        style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
        className={cn("flex-shrink-0 shadow-sm border transition-transform pointer-events-auto cursor-grab active:cursor-grabbing",
          isMerge ? "w-3.5 h-3.5 bg-amber-500 border-amber-400 hover:scale-125 rounded-md" : "w-2.5 h-2.5 bg-brand-500 border-brand-400 hover:scale-150 rounded-full"
        )} 
      >
        {isMerge && (
          <ChevronRight size={10} strokeWidth={3} className={cn("text-white", 
            edge === 'top' && "-rotate-90",
            edge === 'bottom' && "rotate-90",
            edge === 'left' && "rotate-180",
            edge === 'right' && ""
          )} />
        )}
      </div>
      {isPreview && (
        <div className={cn("absolute border-brand-500 pointer-events-none z-50",
          edge === 'top' && "top-1 left-0 right-0 border-t-2 border-dashed", 
          edge === 'bottom' && "bottom-1 left-0 right-0 border-b-2 border-dashed",
          edge === 'left' && "left-1 top-0 bottom-0 border-l-2 border-dashed", 
          edge === 'right' && "right-1 top-0 bottom-0 border-r-2 border-dashed"
        )} />
      )}
    </div>
  );
};

const ToolbarButton = ({ icon: Icon, onClick, active, disabled, title }: any) => (
  <button
    onClick={onClick} disabled={disabled} title={title}
    className={cn("w-9 h-9 rounded-md flex items-center justify-center transition-all duration-200 outline-none",
      disabled ? "text-slate-300 cursor-not-allowed" : "text-slate-600 hover:bg-slate-100 active:bg-slate-200",
      active && !disabled ? "bg-brand-100 text-brand-700 hover:bg-brand-200 shadow-sm" : ""
    )}
  >
    <Icon size={18} strokeWidth={active ? 2.5 : 2} />
  </button>
);
