/**
 * Document-model table operations — pure functions over the Table type:
 * row/column add+delete, cell merge/split, selection bounds + lookups,
 * grid-anchor calculations for irregular merged tables. Used by
 * TableToolbar.tsx and the React table-selection hook; published from
 * `@eigenpal/docx-editor-react`.
 */

import type { Table, TableCell, TableRow } from '@eigenpal/docx-editor-core/types/document';
import {
  type CellAnchor,
  computeSplitLayout,
  computeSplitDialogDefaults,
  redistributeColumnWidths,
  buildAnchorMaps,
} from '@eigenpal/docx-editor-core/utils';
import type { TableAction, TableContext, TableSelection, TableSplitConfig } from '../TableToolbar';

// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================

/**
 * Create a table context from a table and selection
 */
export function createTableContext(table: Table, selection: TableSelection): TableContext {
  const rowCount = table.rows.length;
  const columnCount = getColumnCount(table);

  // Check if multi-cell selection
  const hasMultiCellSelection = !!(
    selection.selectedCells &&
    (selection.selectedCells.startRow !== selection.selectedCells.endRow ||
      selection.selectedCells.startCol !== selection.selectedCells.endCol)
  );

  const currentCell = getCellAt(table, selection.rowIndex, selection.columnIndex);
  // Split is available for a single active cell. The UI opens a dialog and
  // applies the requested row/column split explicitly.
  const canSplitCell = !!currentCell && !hasMultiCellSelection;

  return {
    table,
    selection,
    hasMultiCellSelection,
    canSplitCell,
    rowCount,
    columnCount,
  };
}

/**
 * Get column count from a table
 */
export function getColumnCount(table: Table): number {
  if (!table.rows.length) return 0;

  let maxCols = 0;
  for (const row of table.rows) {
    let colCount = 0;
    for (const cell of row.cells) {
      colCount += cell.formatting?.gridSpan ?? 1;
    }
    maxCols = Math.max(maxCols, colCount);
  }
  return maxCols;
}

/**
 * Get cell at specific row and column index
 */
export function getCellAt(table: Table, rowIndex: number, columnIndex: number): TableCell | null {
  const row = table.rows[rowIndex];
  if (!row) return null;

  let currentCol = 0;
  for (const cell of row.cells) {
    const colspan = cell.formatting?.gridSpan ?? 1;
    if (columnIndex >= currentCol && columnIndex < currentCol + colspan) {
      return cell;
    }
    currentCol += colspan;
  }
  return null;
}

/**
 * Check if a selection spans multiple cells
 */
export function isMultiCellSelection(selection: TableSelection): boolean {
  if (!selection.selectedCells) return false;
  const { startRow, startCol, endRow, endCol } = selection.selectedCells;
  return startRow !== endRow || startCol !== endCol;
}

/**
 * Get the bounds of a selection
 */
export function getSelectionBounds(selection: TableSelection): {
  startRow: number;
  startCol: number;
  endRow: number;
  endCol: number;
} {
  if (selection.selectedCells) {
    return selection.selectedCells;
  }
  return {
    startRow: selection.rowIndex,
    startCol: selection.columnIndex,
    endRow: selection.rowIndex,
    endCol: selection.columnIndex,
  };
}

/**
 * Check if a cell is within a selection
 */
export function isCellInSelection(
  rowIndex: number,
  colIndex: number,
  selection: TableSelection
): boolean {
  const bounds = getSelectionBounds(selection);
  return (
    rowIndex >= bounds.startRow &&
    rowIndex <= bounds.endRow &&
    colIndex >= bounds.startCol &&
    colIndex <= bounds.endCol
  );
}

/**
 * Create an empty row with the same structure as an existing row
 */
export function createEmptyRow(templateRow: TableRow, columnCount: number): TableRow {
  const cells: TableCell[] = [];

  // Create cells matching the column structure
  let colIndex = 0;
  for (const templateCell of templateRow.cells) {
    const colspan = templateCell.formatting?.gridSpan ?? 1;
    cells.push({
      type: 'tableCell',
      content: [
        {
          type: 'paragraph' as const,
          content: [],
          formatting: {},
        },
      ],
      formatting: {
        ...templateCell.formatting,
        vMerge: undefined, // Don't copy vertical merge
      },
    });
    colIndex += colspan;
  }

  // If template row has fewer columns, add more cells
  while (colIndex < columnCount) {
    cells.push({
      type: 'tableCell',
      content: [
        {
          type: 'paragraph' as const,
          content: [],
          formatting: {},
        },
      ],
      formatting: {},
    });
    colIndex++;
  }

  return {
    type: 'tableRow',
    cells,
    formatting: {
      ...templateRow.formatting,
      header: false, // New rows aren't headers by default
    },
  };
}

/**
 * Create an empty cell
 */
export function createEmptyCell(): TableCell {
  return {
    type: 'tableCell',
    content: [
      {
        type: 'paragraph',
        content: [],
        formatting: {},
      },
    ],
    formatting: {},
  };
}

// ---------------------------------------------------------------------------
// Document-model table anchor collection
// ---------------------------------------------------------------------------

function getRowCellStartingAt(row: TableRow, targetCol: number): TableCell | null {
  let currentCol = 0;
  for (const cell of row.cells) {
    const colspan = cell.formatting?.gridSpan ?? 1;
    if (currentCol === targetCol) {
      return cell;
    }
    currentCol += colspan;
  }
  return null;
}

function collectDocumentTableAnchors(table: Table): {
  anchors: CellAnchor<TableCell>[];
  totalCols: number;
} {
  const anchors: CellAnchor<TableCell>[] = [];
  let totalCols = 0;

  for (let rowIndex = 0; rowIndex < table.rows.length; rowIndex++) {
    const row = table.rows[rowIndex];
    let colIndex = 0;

    for (const cell of row.cells) {
      const colspan = cell.formatting?.gridSpan ?? 1;
      if (cell.formatting?.vMerge !== 'continue') {
        let rowspan = 1;
        if (cell.formatting?.vMerge === 'restart') {
          for (let nextRow = rowIndex + 1; nextRow < table.rows.length; nextRow++) {
            const continuation = getRowCellStartingAt(table.rows[nextRow], colIndex);
            if (!continuation || continuation.formatting?.vMerge !== 'continue') break;
            rowspan += 1;
          }
        }

        anchors.push({ data: cell, row: rowIndex, col: colIndex, rowspan, colspan });
      }

      colIndex += colspan;
      totalCols = Math.max(totalCols, colIndex);
    }
  }

  return { anchors, totalCols };
}

// ---------------------------------------------------------------------------
// Document-model cell formatting helpers
// ---------------------------------------------------------------------------

function toAnchorCellFormatting(cell: TableCell, colspan: number, rowspan: number) {
  const formatting = { ...(cell.formatting ?? {}) };
  if (colspan > 1) formatting.gridSpan = colspan;
  else delete formatting.gridSpan;
  if (rowspan > 1) formatting.vMerge = 'restart';
  else delete formatting.vMerge;
  return Object.keys(formatting).length ? formatting : undefined;
}

function toContinuationFormatting(cell: TableCell, colspan: number) {
  const formatting = { ...(cell.formatting ?? {}) };
  if (colspan > 1) formatting.gridSpan = colspan;
  else delete formatting.gridSpan;
  formatting.vMerge = 'continue';
  return formatting;
}

// ---------------------------------------------------------------------------
// Dialog config + split — delegates to shared algorithm
// ---------------------------------------------------------------------------

export function getTableSplitCellDialogConfig(
  table: Table,
  rowIndex: number,
  columnIndex: number
): TableSplitConfig | null {
  const { anchors } = collectDocumentTableAnchors(table);
  const anchor = anchors.find(
    (a) =>
      rowIndex >= a.row &&
      rowIndex < a.row + a.rowspan &&
      columnIndex >= a.col &&
      columnIndex < a.col + a.colspan
  );
  if (!anchor) return null;

  return computeSplitDialogDefaults(anchor.rowspan, anchor.colspan);
}

export function splitTableCell(
  table: Table,
  rowIndex: number,
  columnIndex: number,
  rows: number,
  cols: number
): Table {
  if (rows < 1 || cols < 1) return table;
  const { anchors, totalCols } = collectDocumentTableAnchors(table);
  const target = anchors.find(
    (a) =>
      rowIndex >= a.row &&
      rowIndex < a.row + a.rowspan &&
      columnIndex >= a.col &&
      columnIndex < a.col + a.colspan
  );
  if (!target) return table;
  if (rows < target.rowspan || cols < target.colspan) return table;
  if (rows === 1 && cols === 1) return table;

  const existing =
    table.columnWidths && table.columnWidths.length > 0
      ? [...table.columnWidths]
      : Array.from({ length: totalCols }, () => 1440);
  const newColumnWidths = redistributeColumnWidths(existing, target.col, target.colspan, cols);

  const layout = computeSplitLayout(
    anchors,
    target,
    rows,
    cols,
    table.rows.length,
    (isOriginal) => {
      if (isOriginal) {
        return { ...target.data, formatting: toAnchorCellFormatting(target.data, 1, 1) };
      }
      return {
        type: 'tableCell' as const,
        content: [{ type: 'paragraph' as const, content: [], formatting: {} }],
        formatting: toAnchorCellFormatting(target.data, 1, 1),
      };
    }
  );

  const { byStart, byCoveredSlot } = buildAnchorMaps(layout.anchors);

  const targetRowEnd = target.row + target.rowspan;
  const newColCount = totalCols + layout.deltaCols;
  const newRows: TableRow[] = [];

  for (let row = 0; row < layout.newRowCount; row++) {
    const sourceRow =
      row < targetRowEnd
        ? table.rows[row]
        : row < target.row + rows
          ? table.rows[targetRowEnd - 1]
          : table.rows[row - layout.deltaRows];

    const cells: TableCell[] = [];
    for (let col = 0; col < newColCount; ) {
      const anchor = byStart.get(`${row}-${col}`);
      if (anchor) {
        cells.push({
          ...anchor.data,
          formatting: toAnchorCellFormatting(anchor.data, anchor.colspan, anchor.rowspan),
        });
        col += anchor.colspan;
        continue;
      }

      const coveringAnchor = byCoveredSlot.get(`${row}-${col}`);
      if (!coveringAnchor) {
        col += 1;
        continue;
      }

      cells.push({
        ...coveringAnchor.data,
        content: [],
        formatting: toContinuationFormatting(coveringAnchor.data, coveringAnchor.colspan),
      });
      col += coveringAnchor.colspan;
    }

    newRows.push({
      type: 'tableRow',
      formatting: sourceRow?.formatting ? { ...sourceRow.formatting } : undefined,
      cells,
    });
  }

  return {
    ...table,
    rows: newRows,
    columnWidths: newColumnWidths,
  };
}

/**
 * Add a row to a table at the specified index
 */
export function addRow(
  table: Table,
  atIndex: number,
  position: 'before' | 'after' = 'after'
): Table {
  const newRows = [...table.rows];
  const insertIndex = position === 'before' ? atIndex : atIndex + 1;
  const templateRow = table.rows[atIndex] || table.rows[0];
  const columnCount = getColumnCount(table);
  const newRow = createEmptyRow(templateRow, columnCount);

  newRows.splice(insertIndex, 0, newRow);

  return {
    ...table,
    rows: newRows,
  };
}

/**
 * Delete a row from a table
 */
export function deleteRow(table: Table, rowIndex: number): Table {
  if (table.rows.length <= 1) {
    return table; // Don't delete the last row
  }

  const newRows = table.rows.filter((_, index) => index !== rowIndex);

  return {
    ...table,
    rows: newRows,
  };
}

/**
 * Add a column to a table at the specified index
 */
export function addColumn(
  table: Table,
  atIndex: number,
  position: 'before' | 'after' = 'after'
): Table {
  const insertIndex = position === 'before' ? atIndex : atIndex + 1;

  const newRows = table.rows.map((row) => {
    const newCells = [...row.cells];

    // Find the cell at the column index and insert a new cell
    let currentCol = 0;
    let insertCellIndex = 0;

    for (let i = 0; i < row.cells.length; i++) {
      const cell = row.cells[i];
      const colspan = cell.formatting?.gridSpan ?? 1;

      if (insertIndex <= currentCol + colspan) {
        insertCellIndex = position === 'before' ? i : i + 1;
        break;
      }
      currentCol += colspan;
      insertCellIndex = i + 1;
    }

    newCells.splice(insertCellIndex, 0, createEmptyCell());

    return {
      ...row,
      cells: newCells,
    };
  });

  // Update column widths if present
  let newColumnWidths = table.columnWidths;
  if (table.columnWidths && table.columnWidths.length > 0) {
    newColumnWidths = [...table.columnWidths];
    const templateWidth = table.columnWidths[atIndex] || table.columnWidths[0] || 1440; // Default 1 inch
    newColumnWidths.splice(insertIndex, 0, templateWidth);
  }

  return {
    ...table,
    rows: newRows,
    columnWidths: newColumnWidths,
  };
}

/**
 * Delete a column from a table
 */
export function deleteColumn(table: Table, columnIndex: number): Table {
  const columnCount = getColumnCount(table);
  if (columnCount <= 1) {
    return table; // Don't delete the last column
  }

  const newRows = table.rows.map((row) => {
    let currentCol = 0;
    const newCells: TableCell[] = [];

    for (const cell of row.cells) {
      const colspan = cell.formatting?.gridSpan ?? 1;

      // Check if this cell spans the column to delete
      if (columnIndex >= currentCol && columnIndex < currentCol + colspan) {
        if (colspan > 1) {
          // Reduce gridSpan by 1
          newCells.push({
            ...cell,
            formatting: {
              ...cell.formatting,
              gridSpan: colspan - 1,
            },
          });
        }
        // If colspan is 1, skip this cell (delete it)
      } else {
        newCells.push(cell);
      }

      currentCol += colspan;
    }

    return {
      ...row,
      cells: newCells,
    };
  });

  // Update column widths if present
  let newColumnWidths = table.columnWidths;
  if (table.columnWidths && table.columnWidths.length > columnIndex) {
    newColumnWidths = table.columnWidths.filter((_, i) => i !== columnIndex);
  }

  return {
    ...table,
    rows: newRows,
    columnWidths: newColumnWidths,
  };
}

/**
 * Merge cells in a selection
 */
export function mergeCells(table: Table, selection: TableSelection): Table {
  if (!selection.selectedCells) {
    return table;
  }

  const { startRow, startCol, endRow, endCol } = selection.selectedCells;
  const rowSpan = endRow - startRow + 1;
  const colSpan = endCol - startCol + 1;

  // Create new rows with merged cell
  const newRows = table.rows.map((row, rowIndex) => {
    if (rowIndex < startRow || rowIndex > endRow) {
      return row;
    }

    const newCells: TableCell[] = [];
    let currentCol = 0;

    for (const cell of row.cells) {
      const cellColSpan = cell.formatting?.gridSpan ?? 1;
      const cellEndCol = currentCol + cellColSpan - 1;

      // Check if this cell is in the selection
      const inSelection = currentCol <= endCol && cellEndCol >= startCol;

      if (!inSelection) {
        newCells.push(cell);
      } else if (rowIndex === startRow && currentCol === startCol) {
        // This is the top-left cell - it becomes the merged cell
        newCells.push({
          ...cell,
          formatting: {
            ...cell.formatting,
            gridSpan: colSpan,
            vMerge: rowSpan > 1 ? 'restart' : undefined,
          },
        });
      } else if (rowIndex > startRow && currentCol === startCol) {
        // Cells below the first row in merge area
        newCells.push({
          ...cell,
          formatting: {
            ...cell.formatting,
            gridSpan: colSpan,
            vMerge: 'continue',
          },
        });
      }
      // Skip other cells in the selection

      currentCol += cellColSpan;
    }

    return {
      ...row,
      cells: newCells,
    };
  });

  return {
    ...table,
    rows: newRows,
  };
}

/**
 * Backward-compatible helper for callers that still use the older merged-cell
 * split behavior directly.
 *
 * User-facing Split cell is now dialog-driven. For document-model tables, use
 * `getTableSplitCellDialogConfig()` and `splitTableCell()` instead.
 */
export function splitCell(table: Table, rowIndex: number, columnIndex: number): Table {
  const cell = getCellAt(table, rowIndex, columnIndex);
  if (!cell) return table;

  const gridSpan = cell.formatting?.gridSpan ?? 1;
  const isVMergeStart = cell.formatting?.vMerge === 'restart';

  if (gridSpan <= 1 && !isVMergeStart) {
    return table; // Nothing to split
  }

  const newRows = table.rows.map((row, rIndex) => {
    if (rIndex !== rowIndex && !isVMergeStart) {
      return row;
    }

    const newCells: TableCell[] = [];
    let currentCol = 0;

    for (const rowCell of row.cells) {
      const cellColSpan = rowCell.formatting?.gridSpan ?? 1;

      if (
        currentCol === columnIndex ||
        (currentCol <= columnIndex && columnIndex < currentCol + cellColSpan)
      ) {
        // This is the cell to split
        if (gridSpan > 1) {
          // Split horizontally
          for (let i = 0; i < gridSpan; i++) {
            newCells.push({
              type: 'tableCell',
              content:
                i === 0
                  ? rowCell.content
                  : [{ type: 'paragraph' as const, content: [], formatting: {} }],
              formatting: {
                ...rowCell.formatting,
                gridSpan: undefined,
                vMerge: undefined,
              },
            });
          }
        } else if (isVMergeStart && rIndex === rowIndex) {
          // Remove vMerge from this cell
          newCells.push({
            ...rowCell,
            formatting: {
              ...rowCell.formatting,
              vMerge: undefined,
            },
          });
        } else if (rowCell.formatting?.vMerge === 'continue') {
          // This row was part of vMerge - restore as regular cell
          newCells.push({
            type: 'tableCell',
            content: [{ type: 'paragraph' as const, content: [], formatting: {} }],
            formatting: {
              ...rowCell.formatting,
              vMerge: undefined,
            },
          });
        } else {
          newCells.push(rowCell);
        }
      } else {
        newCells.push(rowCell);
      }

      currentCol += cellColSpan;
    }

    return {
      ...row,
      cells: newCells,
    };
  });

  return {
    ...table,
    rows: newRows,
  };
}

/** Simple (string) table actions */
type SimpleTableAction = Exclude<TableAction, { type: string }>;

/**
 * Get action label for display
 */
export function getActionLabel(action: TableAction): string {
  if (typeof action === 'object') {
    if (action.type === 'cellFillColor') return 'Cell Fill Color';
    if (action.type === 'borderColor') return 'Border Color';
    return 'Unknown Action';
  }

  const labels: Record<SimpleTableAction, string> = {
    addRowAbove: 'Insert Row Above',
    addRowBelow: 'Insert Row Below',
    addColumnLeft: 'Insert Column Left',
    addColumnRight: 'Insert Column Right',
    deleteRow: 'Delete Row',
    deleteColumn: 'Delete Column',
    mergeCells: 'Merge Cells',
    splitCell: 'Split Cell',
    deleteTable: 'Delete Table',
    selectTable: 'Select Table',
    selectRow: 'Select Row',
    selectColumn: 'Select Column',
    borderAll: 'All Borders',
    borderOutside: 'Outside Borders',
    borderInside: 'Inside Borders',
    borderNone: 'No Borders',
    borderTop: 'Top Border',
    borderBottom: 'Bottom Border',
    borderLeft: 'Left Border',
    borderRight: 'Right Border',
  };
  return labels[action];
}

/**
 * Check if an action is a delete action
 */
export function isDeleteAction(action: TableAction): boolean {
  return (
    typeof action === 'string' &&
    (action === 'deleteRow' || action === 'deleteColumn' || action === 'deleteTable')
  );
}

/**
 * Handle keyboard shortcuts for table actions
 */
export function handleTableShortcut(
  _event: KeyboardEvent,
  context: TableContext | null
): TableAction | null {
  if (!context) return null;

  // No default keyboard shortcuts defined for table operations
  // This function can be extended to add shortcuts like:
  // - Ctrl+Shift+R for add row
  // - Ctrl+Shift+C for add column
  // etc.

  return null;
}
