use serde_json::{Map, Value};

use crate::model::block::{BlockAlignment, BlockId};

/// Direction for navigating between table cells.
#[derive(Debug, Clone)]
pub enum TableNavDirection {
    NextCell,
    PrevCell,
    Up,
    Down,
    Left,
    Right,
}

/// Direction for splitting a table cell.
#[derive(Debug, Clone)]
pub enum SplitDirection {
    /// Split into N rows.
    Horizontal(usize),
    /// Split into N columns.
    Vertical(usize),
    /// Split into an R×C grid.
    Grid(usize, usize),
}

#[derive(Debug, Clone)]
pub enum Command {
    InsertText {
        text: String,
    },
    Backspace,
    Delete,
    Enter,
    ToggleMark {
        mark_type: String,
    },
    SetMark {
        mark_type: String,
        attrs: Map<String, Value>,
    },
    ClearMark {
        mark_type: String,
    },
    SetBlockType {
        block_type: String,
        props: Map<String, Value>,
    },
    /// Set the text alignment of every block intersected by the current selection.
    /// [`BlockAlignment::Left`] is the default and clears the stored property.
    SetBlockAlign {
        align: BlockAlignment,
    },
    ToggleTodo,
    /// Indent the current list block (Tab). No-op if previous sibling is not a list.
    IndentBlock,
    /// Outdent the current list block (Shift+Tab). No-op if already at indent 0.
    OutdentBlock,
    /// Guarantee an empty paragraph immediately AFTER the root block that
    /// currently contains the cursor (walking up the parent chain when the
    /// cursor is inside a nested block such as a table cell). No-op when
    /// the next root block is already an empty paragraph. Caret position
    /// is intentionally preserved so callers can compose this with other
    /// commands (e.g. `[InsertTable, EnsureTrailingParagraph]` keeps the
    /// caret in the first cell while still producing a trailing escape
    /// block below the table).
    EnsureTrailingParagraph,

    // --- Table commands ---
    /// Insert an N×M table at the current selection.
    InsertTable {
        rows: usize,
        cols: usize,
    },
    /// Insert a row above or below `row_index` in the given table.
    InsertTableRow {
        table_id: BlockId,
        row_index: usize,
        above: bool,
    },
    /// Delete a row from the given table.
    DeleteTableRow {
        table_id: BlockId,
        row_index: usize,
    },
    /// Insert a column to the left or right of `col_index`.
    InsertTableCol {
        table_id: BlockId,
        col_index: usize,
        left: bool,
    },
    /// Delete a column from the given table.
    DeleteTableCol {
        table_id: BlockId,
        col_index: usize,
    },
    /// Merge the currently selected cell range.
    MergeCells {
        table_id: BlockId,
    },
    /// Unmerge a previously merged cell.
    UnmergeCells {
        table_id: BlockId,
        cell_id: BlockId,
    },
    /// Split a single cell in the given direction.
    SplitCell {
        table_id: BlockId,
        cell_id: BlockId,
        direction: SplitDirection,
    },
    /// Update props on one or more cells (style, alignment, etc.).
    SetCellProps {
        cell_ids: Vec<BlockId>,
        props: Map<String, Value>,
    },
    /// Update props on an entire table block (background/border defaults that
    /// individual cells can override, plus table-level column definitions).
    ///
    /// Uses the same `SetBlockProps` operation as [`Self::SetCellProps`] under
    /// the hood — the only difference is the validation target (the block
    /// must be a `table`, not a `table_cell`) and the fact that a single
    /// `table_id` is addressed instead of a cell list.
    SetTableProps {
        table_id: BlockId,
        props: Map<String, Value>,
    },
    /// Update props on a table row block (currently: `height`).
    ///
    /// Same operation family as [`Self::SetTableProps`]; the only difference
    /// is the target block must be a `table_row`. Exposed as a distinct
    /// command so the clipboard round-trip can restore row heights after
    /// `InsertTable` (which produces rows with default heights).
    SetRowProps {
        row_id: BlockId,
        props: Map<String, Value>,
    },
    /// Toggle a mark across the full text range of one or more selected cells.
    ToggleCellMark {
        cell_ids: Vec<BlockId>,
        mark_type: String,
    },
    /// Clear text contents of one or more cells in a single undoable command.
    ClearCells {
        cell_ids: Vec<BlockId>,
    },
    /// Navigate between table cells via keyboard.
    TableNavigate {
        direction: TableNavDirection,
    },
}
