//! Table-specific command transforms and helpers.
//!
//! Split out of `transform/mod.rs` so that file stays under 1000 lines.
//! Everything here is `pub(super)` because `super::transform()` dispatches
//! to these functions for every table-related `Command` variant.
//!
//! Shared plumbing:
//!   - `super::set_block_props_op` — used by both non-table and table paths.
//!   - `super::block_types::*`, `super::prop_keys::*` via the parent `use`s.

use serde_json::{Map, Value};

use super::{
    Block, BlockId, Document, EditorError, IdGenerator, Mark, Operation, Selection, block_types,
    pending_marks_at, pending_marks_props_op, prop_keys, set_block_props_op,
};
use crate::model::selection::CellSelection;

/// Create an N×M table at the current selection position.
///
/// Generates operations:
/// 1. InsertBlock for the table block (into root)
/// 2. InsertChildBlock for each row (under table)
/// 3. InsertChildBlock for each cell (under each row)
/// 4. SetSelection to the first cell
pub(super) fn transform_insert_table(
    doc: &Document,
    selection: &Selection,
    rows: usize,
    cols: usize,
    id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let rows = rows.max(1);
    let cols = cols.max(1);

    let mut ops = Vec::new();

    // Determine insertion index in root: after the current block
    let insert_index = doc
        .block_root_index(&selection.anchor.id)
        .map(|idx| idx + 1)
        .unwrap_or(doc.root_block_count());

    // Create table block with column width definitions
    let table_id = id_gen.next_id();
    let columns: Vec<Value> = (0..cols)
        .map(|_| {
            let mut col = Map::new();
            col.insert("width".into(), Value::Number(120.into()));
            Value::Object(col)
        })
        .collect();
    let table_block = Block::new_table(table_id.clone(), columns);

    ops.push(Operation::InsertBlock {
        block: table_block,
        index: insert_index,
    });

    // Track the first cell ID for selection
    let mut first_cell_id: Option<BlockId> = None;

    for row_idx in 0..rows {
        let row_id = id_gen.next_id();
        let row_block = Block::new_table_row(row_id.clone());

        ops.push(Operation::InsertChildBlock {
            parent_id: table_id.clone(),
            block: row_block,
            index: row_idx,
        });

        for col_idx in 0..cols {
            let cell_id = id_gen.next_id();
            let cell_block = Block::new_table_cell(cell_id.clone());

            if first_cell_id.is_none() {
                first_cell_id = Some(cell_id.clone());
            }

            ops.push(Operation::InsertChildBlock {
                parent_id: row_id.clone(),
                block: cell_block,
                index: col_idx,
            });
        }
    }

    // Move cursor to the first cell
    //
    // Note: callers that want a trailing paragraph below the table —
    // every user-initiated insertion path (slash menu, toolbar grid
    // picker) — dispatch `Command::EnsureTrailingParagraph` AFTER
    // this transform. Paste deliberately does NOT, because it
    // reproduces the source document structure verbatim and already
    // preserves whatever surrounded the original table. Centralising
    // the trailing decision at the dispatch layer instead of here
    // avoids the regression where pasting a table inserted a second
    // empty paragraph after the clone (broke every
    // `editor-copy-paste-tables` test).
    if let Some(cell_id) = first_cell_id {
        ops.push(Operation::SetSelection {
            selection: Some(Selection::collapsed(cell_id, 0)),
        });
    }

    Ok(ops)
}

/// Return only the entries of `props` whose value differs from the block's
/// current value for that key. Used by SetCellProps / SetTableProps /
/// SetRowProps so that dispatching the same value twice becomes a no-op
/// (nothing to put on the history stack, nothing to render).
pub(super) fn filter_changed_props(
    block: &Block,
    props: &Map<String, Value>,
) -> Map<String, Value> {
    let mut merged = Map::new();
    for (key, value) in props {
        if block.props.get(key) != Some(value) {
            merged.insert(key.clone(), value.clone());
        }
    }
    merged
}

pub(super) fn table_columns(table: &Block) -> Vec<Value> {
    table
        .props
        .get("columns")
        .and_then(Value::as_array)
        .cloned()
        .unwrap_or_default()
}

pub(super) fn find_table_context(
    doc: &Document,
    cell_id: &BlockId,
) -> Result<Option<(BlockId, BlockId)>, EditorError> {
    let Some(cell) = doc.get_block(cell_id) else {
        return Err(EditorError::BlockNotFound(cell_id.clone()));
    };
    if !cell.is_table_cell() {
        return Ok(None);
    }

    let Some(row_id) = cell.parent.clone() else {
        return Ok(None);
    };
    let row = doc.require_block(&row_id)?;
    let Some(table_id) = row.parent.clone() else {
        return Ok(None);
    };
    Ok(Some((table_id, row_id)))
}

pub(super) fn make_default_column() -> Value {
    let mut col = Map::new();
    col.insert("width".into(), Value::Number(120.into()));
    Value::Object(col)
}

pub(super) fn selection_for_cell(cell_id: BlockId) -> Operation {
    Operation::SetSelection {
        selection: Some(Selection::collapsed(cell_id, 0)),
    }
}

pub(super) fn transform_insert_table_row(
    doc: &Document,
    table_id: &BlockId,
    row_index: usize,
    above: bool,
    id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let table = doc.require_block(table_id)?;
    let row_count = table.children.len();
    let insert_index = if above {
        row_index.min(row_count)
    } else {
        row_index.saturating_add(1).min(row_count)
    };

    let col_count = table
        .children
        .first()
        .and_then(|row_id| doc.get_block(row_id))
        .map(|row| row.children.len())
        .unwrap_or_else(|| table_columns(table).len().max(1));

    let row_id = id_gen.next_id();
    let row_block = Block::new_table_row(row_id.clone());
    let mut ops = vec![Operation::InsertChildBlock {
        parent_id: table_id.clone(),
        block: row_block,
        index: insert_index,
    }];

    let mut first_cell_id = None;
    for col_idx in 0..col_count.max(1) {
        let cell_id = id_gen.next_id();
        if first_cell_id.is_none() {
            first_cell_id = Some(cell_id.clone());
        }
        ops.push(Operation::InsertChildBlock {
            parent_id: row_id.clone(),
            block: Block::new_table_cell(cell_id),
            index: col_idx,
        });
    }

    if let Some(cell_id) = first_cell_id {
        ops.push(selection_for_cell(cell_id));
    }

    Ok(ops)
}

pub(super) fn transform_delete_table_row(
    doc: &Document,
    table_id: &BlockId,
    row_index: usize,
    id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let table = doc.require_block(table_id)?;
    let row_id = table
        .children
        .get(row_index)
        .cloned()
        .ok_or_else(|| EditorError::InvalidOperation("table row index out of bounds".into()))?;
    let row = doc.require_block(&row_id)?;

    let mut ops = Vec::new();
    if table.children.len() == 1 {
        for cell_id in &row.children {
            ops.push(Operation::RemoveChildBlock {
                parent_id: row_id.clone(),
                block_id: cell_id.clone(),
            });
        }
        ops.push(Operation::RemoveChildBlock {
            parent_id: table_id.clone(),
            block_id: row_id,
        });
        ops.push(Operation::RemoveBlock {
            block_id: table_id.clone(),
        });

        let paragraph_id = id_gen.next_id();
        let index = doc
            .block_root_index(table_id)
            .unwrap_or(doc.root_block_count());
        ops.push(Operation::InsertBlock {
            block: Block::new_paragraph(paragraph_id.clone()),
            index,
        });
        ops.push(selection_for_cell(paragraph_id));
        return Ok(ops);
    }

    for cell_id in &row.children {
        ops.push(Operation::RemoveChildBlock {
            parent_id: row_id.clone(),
            block_id: cell_id.clone(),
        });
    }
    ops.push(Operation::RemoveChildBlock {
        parent_id: table_id.clone(),
        block_id: row_id,
    });

    let target_row_id = table.children.get(row_index + 1).cloned().or_else(|| {
        row_index
            .checked_sub(1)
            .and_then(|idx| table.children.get(idx).cloned())
    });
    if let Some(target_row_id) = target_row_id
        && let Some(first_cell_id) = doc
            .get_block(&target_row_id)
            .and_then(|target_row| target_row.children.first())
            .cloned()
    {
        ops.push(selection_for_cell(first_cell_id));
    }

    Ok(ops)
}

pub(super) fn transform_insert_table_col(
    doc: &Document,
    table_id: &BlockId,
    col_index: usize,
    left: bool,
    id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let table = doc.require_block(table_id)?;
    let row_ids = table.children.clone();
    let insert_index = if left {
        col_index
    } else {
        col_index.saturating_add(1)
    };

    let mut ops = Vec::new();
    for row_id in row_ids {
        let row = doc.require_block(&row_id)?;
        ops.push(Operation::InsertChildBlock {
            parent_id: row_id,
            block: Block::new_table_cell(id_gen.next_id()),
            index: insert_index.min(row.children.len()),
        });
    }

    let mut columns = table_columns(table);
    let idx = insert_index.min(columns.len());
    columns.insert(idx, make_default_column());
    let mut props = Map::new();
    props.insert("columns".into(), Value::Array(columns));
    ops.push(set_block_props_op(doc, table_id, props)?);
    Ok(ops)
}

pub(super) fn remove_table_ops(
    doc: &Document,
    table_id: &BlockId,
) -> Result<Vec<Operation>, EditorError> {
    let table = doc.require_block(table_id)?;
    let mut ops = Vec::new();
    for row_id in &table.children {
        let row = doc.require_block(row_id)?;
        for cell_id in &row.children {
            ops.push(Operation::RemoveChildBlock {
                parent_id: row_id.clone(),
                block_id: cell_id.clone(),
            });
        }
        ops.push(Operation::RemoveChildBlock {
            parent_id: table_id.clone(),
            block_id: row_id.clone(),
        });
    }
    ops.push(Operation::RemoveBlock {
        block_id: table_id.clone(),
    });
    Ok(ops)
}

pub(super) fn transform_delete_table_col(
    doc: &Document,
    table_id: &BlockId,
    col_index: usize,
    id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let table = doc.require_block(table_id)?;
    let row_ids = table.children.clone();
    let col_count = row_ids
        .first()
        .and_then(|row_id| doc.get_block(row_id))
        .map(|row| row.children.len())
        .unwrap_or_default();
    if col_index >= col_count {
        return Err(EditorError::InvalidOperation(
            "table column index out of bounds".into(),
        ));
    }

    if col_count == 1 {
        let mut ops = remove_table_ops(doc, table_id)?;
        let paragraph_id = id_gen.next_id();
        let index = doc
            .block_root_index(table_id)
            .unwrap_or(doc.root_block_count());
        ops.push(Operation::InsertBlock {
            block: Block::new_paragraph(paragraph_id.clone()),
            index,
        });
        ops.push(selection_for_cell(paragraph_id));
        return Ok(ops);
    }

    let mut ops = Vec::new();
    for row_id in row_ids {
        let row = doc.require_block(&row_id)?;
        let cell_id = row.children.get(col_index).cloned().ok_or_else(|| {
            EditorError::InvalidOperation("table column index out of bounds".into())
        })?;
        ops.push(Operation::RemoveChildBlock {
            parent_id: row_id,
            block_id: cell_id,
        });
    }

    let mut columns = table_columns(table);
    if col_index < columns.len() {
        columns.remove(col_index);
    }
    let mut props = Map::new();
    props.insert("columns".into(), Value::Array(columns));
    ops.push(set_block_props_op(doc, table_id, props)?);
    Ok(ops)
}

pub(super) fn transform_merge_cells(
    doc: &Document,
    selection: &Selection,
    table_id: &BlockId,
) -> Result<Vec<Operation>, EditorError> {
    let Some(cell_selection) = selection.cell_selection.as_ref() else {
        return Ok(Vec::new());
    };
    if &cell_selection.table_id != table_id {
        return Ok(Vec::new());
    }

    let grid = doc.table_cell_grid(table_id);
    let (top_left, bottom_right) = cell_selection.normalized();
    let Some(master_cell_id) = grid
        .get(top_left.row)
        .and_then(|row| row.get(top_left.col))
        .cloned()
    else {
        return Ok(Vec::new());
    };

    let master = doc.require_block(&master_cell_id)?;

    let mut ops = Vec::new();
    let mut master_props = Map::new();
    master_props.insert(
        "colspan".into(),
        Value::Number((bottom_right.col - top_left.col + 1).into()),
    );
    master_props.insert(
        "rowspan".into(),
        Value::Number((bottom_right.row - top_left.row + 1).into()),
    );
    ops.push(set_block_props_op(doc, &master_cell_id, master_props)?);

    let mut appended_texts = Vec::new();
    for row in top_left.row..=bottom_right.row {
        for col in top_left.col..=bottom_right.col {
            let Some(cell_id) = grid.get(row).and_then(|cells| cells.get(col)).cloned() else {
                continue;
            };
            if cell_id == master_cell_id {
                continue;
            }
            let cell = doc.require_block(&cell_id)?;

            let mut props = Map::new();
            props.insert(
                prop_keys::COVERED_BY.into(),
                Value::String(master_cell_id.0.clone()),
            );
            ops.push(set_block_props_op(doc, &cell_id, props)?);

            let text = cell.plain_text();
            if !text.is_empty() {
                appended_texts.push(text);
                ops.push(Operation::DeleteRange {
                    block_id: cell_id,
                    start: 0,
                    end: cell.text_len(),
                });
            }
        }
    }

    if !appended_texts.is_empty() {
        let joined = appended_texts.join("\n");
        let text = if master.text_len() == 0 {
            joined
        } else {
            format!("\n{joined}")
        };
        ops.push(Operation::InsertTextAt {
            block_id: master_cell_id,
            offset: master.text_len(),
            text,
            marks: Vec::new(),
        });
    }

    Ok(ops)
}

pub(super) fn transform_unmerge_cells(
    doc: &Document,
    table_id: &BlockId,
    cell_id: &BlockId,
) -> Result<Vec<Operation>, EditorError> {
    let table = doc.require_block(table_id)?;
    let mut ops = Vec::new();

    let mut master_props = Map::new();
    master_props.insert("colspan".into(), Value::Number(1.into()));
    master_props.insert("rowspan".into(), Value::Number(1.into()));
    ops.push(set_block_props_op(doc, cell_id, master_props)?);

    for row_id in &table.children {
        let row = doc.require_block(row_id)?;
        for covered_cell_id in &row.children {
            let cell = doc.require_block(covered_cell_id)?;
            if cell.covered_by() == Some(cell_id.0.as_str()) {
                let mut props = Map::new();
                props.insert(prop_keys::COVERED_BY.into(), Value::Null);
                ops.push(set_block_props_op(doc, covered_cell_id, props)?);
            }
        }
    }

    Ok(ops)
}

pub(super) fn transform_split_cell(
    doc: &Document,
    table_id: &BlockId,
    cell_id: &BlockId,
    direction: &crate::command::command::SplitDirection,
    _id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let grid = doc.table_cell_grid(table_id);
    let Some((row_idx, col_idx)) = grid.iter().enumerate().find_map(|(row_idx, row)| {
        row.iter()
            .position(|id| id == cell_id)
            .map(|col_idx| (row_idx, col_idx))
    }) else {
        return Ok(Vec::new());
    };

    let cell = doc.require_block(cell_id)?;
    let mut ops = Vec::new();

    match direction {
        crate::command::command::SplitDirection::Vertical(_) => {
            if cell.colspan() <= 1 {
                return Ok(Vec::new());
            }
            let mut props = Map::new();
            props.insert("colspan".into(), Value::Number(1.into()));
            ops.push(set_block_props_op(doc, cell_id, props)?);
            for offset in 1..cell.colspan() {
                if let Some(covered_cell_id) = grid
                    .get(row_idx)
                    .and_then(|row| row.get(col_idx + offset))
                    .cloned()
                {
                    let mut uncover = Map::new();
                    uncover.insert(prop_keys::COVERED_BY.into(), Value::Null);
                    ops.push(set_block_props_op(doc, &covered_cell_id, uncover)?);
                }
            }
        }
        crate::command::command::SplitDirection::Horizontal(_) => {
            if cell.rowspan() <= 1 {
                return Ok(Vec::new());
            }
            let mut props = Map::new();
            props.insert("rowspan".into(), Value::Number(1.into()));
            ops.push(set_block_props_op(doc, cell_id, props)?);
            for offset in 1..cell.rowspan() {
                if let Some(covered_cell_id) = grid
                    .get(row_idx + offset)
                    .and_then(|row| row.get(col_idx))
                    .cloned()
                {
                    let mut uncover = Map::new();
                    uncover.insert(prop_keys::COVERED_BY.into(), Value::Null);
                    ops.push(set_block_props_op(doc, &covered_cell_id, uncover)?);
                }
            }
        }
        crate::command::command::SplitDirection::Grid(_, _) => {
            ops.extend(transform_split_cell(
                doc,
                table_id,
                cell_id,
                &crate::command::command::SplitDirection::Horizontal(2),
                _id_gen,
            )?);
            ops.extend(transform_split_cell(
                doc,
                table_id,
                cell_id,
                &crate::command::command::SplitDirection::Vertical(2),
                _id_gen,
            )?);
        }
    }

    Ok(ops)
}

pub(super) fn selected_cell_ids(
    doc: &Document,
    selection: &Selection,
    cell_ids: &[BlockId],
) -> Vec<BlockId> {
    let mut ids = cell_ids.to_vec();
    if let Some(CellSelection { table_id, .. }) = &selection.cell_selection {
        let grid = doc.table_cell_grid(table_id);
        if let Some(cell_selection) = &selection.cell_selection {
            let (top_left, bottom_right) = cell_selection.normalized();
            for row in top_left.row..=bottom_right.row {
                for col in top_left.col..=bottom_right.col {
                    if let Some(cell_id) = grid.get(row).and_then(|cells| cells.get(col)).cloned()
                        && !ids.contains(&cell_id)
                    {
                        ids.push(cell_id);
                    }
                }
            }
        }
    }
    ids
}

pub(super) fn transform_set_cell_props(
    doc: &Document,
    selection: &Selection,
    cell_ids: &[BlockId],
    props: &Map<String, Value>,
) -> Result<Vec<Operation>, EditorError> {
    selected_cell_ids(doc, selection, cell_ids)
        .into_iter()
        .map(|cell_id| {
            let block = doc.require_block(&cell_id)?;
            let merged = filter_changed_props(block, props);
            if merged.is_empty() {
                Ok(None)
            } else {
                Ok(Some(set_block_props_op(doc, &cell_id, merged)?))
            }
        })
        .filter_map(Result::transpose)
        .collect()
}

/// Shared implementation for [`transform_set_table_props`] and
/// [`transform_set_row_props`]. Validates that the target block has the
/// expected `block_type`, filters out no-op changes, and returns the
/// `SetBlockProps` op (or an empty vec if there was nothing to change).
///
/// Values explicitly set to `Value::Null` are treated as "clear this prop" —
/// the key still ends up in the merged map so the applier can overwrite with
/// null, which the renderer interprets as "fall back to default".
pub(super) fn transform_set_typed_block_props(
    doc: &Document,
    block_id: &BlockId,
    props: &Map<String, Value>,
    expected_type: &str,
    command_name: &str,
) -> Result<Vec<Operation>, EditorError> {
    let block = doc.require_block(block_id)?;
    if block.ty != expected_type {
        return Err(EditorError::InvalidOperation(format!(
            "{command_name} target {block_id} is not a {expected_type} (got {})",
            block.ty
        )));
    }
    let merged = filter_changed_props(block, props);
    if merged.is_empty() {
        Ok(Vec::new())
    } else {
        Ok(vec![set_block_props_op(doc, block_id, merged)?])
    }
}

/// Update props on an entire table block.
pub(super) fn transform_set_table_props(
    doc: &Document,
    table_id: &BlockId,
    props: &Map<String, Value>,
) -> Result<Vec<Operation>, EditorError> {
    transform_set_typed_block_props(doc, table_id, props, block_types::TABLE, "SetTableProps")
}

/// Update props on a table row block.
pub(super) fn transform_set_row_props(
    doc: &Document,
    row_id: &BlockId,
    props: &Map<String, Value>,
) -> Result<Vec<Operation>, EditorError> {
    transform_set_typed_block_props(doc, row_id, props, block_types::TABLE_ROW, "SetRowProps")
}

pub(super) fn transform_toggle_cell_mark(
    doc: &Document,
    selection: &Selection,
    cell_ids: &[BlockId],
    mark_type: &str,
) -> Result<Vec<Operation>, EditorError> {
    let selected_cell_ids = selected_cell_ids(doc, selection, cell_ids);
    let mut ranges = Vec::new();
    let mut empty_cell_ids = Vec::new();

    for cell_id in selected_cell_ids {
        let Some(block) = doc.get_block(&cell_id) else {
            continue;
        };
        let text_len = block.text_len();
        if text_len > 0 {
            ranges.push((cell_id, 0, text_len));
        } else {
            empty_cell_ids.push(cell_id);
        }
    }

    if ranges.is_empty() && empty_cell_ids.is_empty() {
        return Ok(Vec::new());
    }

    let filled_cells_should_clear = ranges.iter().all(|(block_id, start, end)| {
        doc.get_block(block_id)
            .map(|block| {
                crate::model::inline::all_have_mark(&block.content, *start, *end, mark_type)
            })
            .unwrap_or(false)
    });
    let empty_cells_should_clear = empty_cell_ids.iter().all(|block_id| {
        pending_marks_at(doc, block_id)
            .iter()
            .any(|mark| mark.ty == mark_type)
    });
    let should_clear = filled_cells_should_clear && empty_cells_should_clear;

    let mut ops: Vec<Operation> = ranges
        .into_iter()
        .map(|(block_id, start, end)| {
            if should_clear {
                Operation::ClearMark {
                    block_id,
                    start,
                    end,
                    mark_type: mark_type.to_string(),
                }
            } else {
                Operation::SetMark {
                    block_id,
                    start,
                    end,
                    mark: Mark::new(mark_type),
                }
            }
        })
        .collect();

    for block_id in empty_cell_ids {
        if let Some(op) = pending_marks_props_op(doc, &block_id, mark_type, should_clear)? {
            ops.push(op);
        }
    }

    Ok(ops)
}

pub(super) fn transform_clear_cells(
    doc: &Document,
    selection: &Selection,
    cell_ids: &[BlockId],
) -> Result<Vec<Operation>, EditorError> {
    selected_cell_ids(doc, selection, cell_ids)
        .into_iter()
        .map(|cell_id| {
            let block = doc.require_block(&cell_id)?;
            let text_len = block.text_len();
            if text_len == 0 {
                Ok(None)
            } else {
                Ok(Some(Operation::DeleteRange {
                    block_id: cell_id,
                    start: 0,
                    end: text_len,
                }))
            }
        })
        .filter_map(Result::transpose)
        .collect()
}

pub(super) fn transform_table_navigate(
    doc: &Document,
    selection: &Selection,
    direction: &crate::command::command::TableNavDirection,
    id_gen: &mut dyn IdGenerator,
) -> Result<Vec<Operation>, EditorError> {
    let Some((table_id, _row_id)) = find_table_context(doc, &selection.anchor.id)? else {
        return Ok(Vec::new());
    };

    let grid = doc.table_cell_grid(&table_id);
    let Some((row_idx, col_idx)) = grid.iter().enumerate().find_map(|(row_idx, row)| {
        row.iter()
            .position(|id| id == &selection.anchor.id)
            .map(|col_idx| (row_idx, col_idx))
    }) else {
        return Ok(Vec::new());
    };

    let row_count = grid.len();
    let col_count = grid.get(row_idx).map_or(0, Vec::len);
    match direction {
        crate::command::command::TableNavDirection::NextCell => {
            if col_idx + 1 < col_count {
                return Ok(vec![selection_for_cell(grid[row_idx][col_idx + 1].clone())]);
            }
            if row_idx + 1 < row_count {
                return Ok(vec![selection_for_cell(grid[row_idx + 1][0].clone())]);
            }
            transform_insert_table_row(doc, &table_id, row_idx, false, id_gen)
        }
        crate::command::command::TableNavDirection::PrevCell => {
            if col_idx > 0 {
                return Ok(vec![selection_for_cell(grid[row_idx][col_idx - 1].clone())]);
            }
            if row_idx > 0 {
                let prev_row = &grid[row_idx - 1];
                return Ok(vec![selection_for_cell(
                    prev_row[prev_row.len() - 1].clone(),
                )]);
            }
            let table_index = doc.block_root_index(&table_id).unwrap_or(0);
            if table_index == 0 {
                return Ok(Vec::new());
            }
            let prev_block_id = doc.root_block_ids()[table_index - 1].clone();
            Ok(vec![Operation::SetSelection {
                selection: Some(Selection::collapsed(
                    prev_block_id.clone(),
                    doc.text_len(&prev_block_id),
                )),
            }])
        }
        crate::command::command::TableNavDirection::Up => {
            if row_idx == 0 {
                Ok(Vec::new())
            } else {
                Ok(vec![selection_for_cell(grid[row_idx - 1][col_idx].clone())])
            }
        }
        crate::command::command::TableNavDirection::Down => {
            if row_idx + 1 >= row_count {
                Ok(Vec::new())
            } else {
                Ok(vec![selection_for_cell(grid[row_idx + 1][col_idx].clone())])
            }
        }
        crate::command::command::TableNavDirection::Left => {
            if col_idx == 0 {
                Ok(Vec::new())
            } else {
                Ok(vec![selection_for_cell(grid[row_idx][col_idx - 1].clone())])
            }
        }
        crate::command::command::TableNavDirection::Right => {
            if col_idx + 1 >= col_count {
                Ok(Vec::new())
            } else {
                Ok(vec![selection_for_cell(grid[row_idx][col_idx + 1].clone())])
            }
        }
    }
}
