use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use ts_rs::TS;

use super::command::{Command, SplitDirection, TableNavDirection};
use crate::model::block::{BlockAlignment, BlockId};

#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub enum TableNavDirectionPayload {
    NextCell,
    PrevCell,
    Up,
    Down,
    Left,
    Right,
}

impl From<TableNavDirectionPayload> for TableNavDirection {
    fn from(value: TableNavDirectionPayload) -> Self {
        match value {
            TableNavDirectionPayload::NextCell => Self::NextCell,
            TableNavDirectionPayload::PrevCell => Self::PrevCell,
            TableNavDirectionPayload::Up => Self::Up,
            TableNavDirectionPayload::Down => Self::Down,
            TableNavDirectionPayload::Left => Self::Left,
            TableNavDirectionPayload::Right => Self::Right,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
#[serde(tag = "type")]
pub enum SplitDirectionPayload {
    #[serde(rename = "horizontal")]
    Horizontal { count: usize },
    #[serde(rename = "vertical")]
    Vertical { count: usize },
    #[serde(rename = "grid")]
    Grid { rows: usize, cols: usize },
}

impl From<SplitDirectionPayload> for SplitDirection {
    fn from(value: SplitDirectionPayload) -> Self {
        match value {
            SplitDirectionPayload::Horizontal { count } => Self::Horizontal(count),
            SplitDirectionPayload::Vertical { count } => Self::Vertical(count),
            SplitDirectionPayload::Grid { rows, cols } => Self::Grid(rows, cols),
        }
    }
}

/// JSON payload protocol for frontend → engine command dispatch.
///
/// This is intentionally serde-/TS-friendly and acts as the Rust source of
/// truth for the generated TypeScript `CommandPayload` surface.
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
#[serde(tag = "type")]
pub enum CommandPayload {
    InsertText {
        text: String,
    },
    Backspace,
    Delete,
    Enter,
    ToggleMark {
        mark: String,
    },
    SetMark {
        mark: String,
        #[serde(default)]
        attrs: Map<String, Value>,
    },
    ClearMark {
        mark: String,
    },
    SetBlockType {
        #[serde(rename = "blockType")]
        block_type: String,
        #[serde(default)]
        props: Map<String, Value>,
    },
    SetBlockAlign {
        align: BlockAlignment,
    },
    ToggleTodo,
    IndentBlock,
    OutdentBlock,
    EnsureTrailingParagraph,
    InsertTable {
        rows: usize,
        cols: usize,
    },
    InsertTableRow {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        #[serde(rename = "rowIndex")]
        row_index: usize,
        above: bool,
    },
    DeleteTableRow {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        #[serde(rename = "rowIndex")]
        row_index: usize,
    },
    InsertTableCol {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        #[serde(rename = "colIndex")]
        col_index: usize,
        left: bool,
    },
    DeleteTableCol {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        #[serde(rename = "colIndex")]
        col_index: usize,
    },
    MergeCells {
        #[serde(rename = "tableId")]
        table_id: BlockId,
    },
    UnmergeCells {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        #[serde(rename = "cellId")]
        cell_id: BlockId,
    },
    SplitCell {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        #[serde(rename = "cellId")]
        cell_id: BlockId,
        direction: SplitDirectionPayload,
    },
    SetCellProps {
        #[serde(rename = "cellIds")]
        cell_ids: Vec<BlockId>,
        props: Map<String, Value>,
    },
    SetTableProps {
        #[serde(rename = "tableId")]
        table_id: BlockId,
        props: Map<String, Value>,
    },
    SetRowProps {
        #[serde(rename = "rowId")]
        row_id: BlockId,
        props: Map<String, Value>,
    },
    ToggleCellMark {
        #[serde(rename = "cellIds")]
        cell_ids: Vec<BlockId>,
        mark: String,
    },
    ClearCells {
        #[serde(rename = "cellIds")]
        cell_ids: Vec<BlockId>,
    },
    TableNavigate {
        direction: TableNavDirectionPayload,
    },
}

impl CommandPayload {
    #[must_use]
    pub fn into_command(self) -> Command {
        match self {
            Self::InsertText { text } => Command::InsertText { text },
            Self::Backspace => Command::Backspace,
            Self::Delete => Command::Delete,
            Self::Enter => Command::Enter,
            Self::ToggleMark { mark } => Command::ToggleMark { mark_type: mark },
            Self::SetMark { mark, attrs } => Command::SetMark {
                mark_type: mark,
                attrs,
            },
            Self::ClearMark { mark } => Command::ClearMark { mark_type: mark },
            Self::SetBlockType { block_type, props } => Command::SetBlockType { block_type, props },
            Self::SetBlockAlign { align } => Command::SetBlockAlign { align },
            Self::ToggleTodo => Command::ToggleTodo,
            Self::IndentBlock => Command::IndentBlock,
            Self::OutdentBlock => Command::OutdentBlock,
            Self::EnsureTrailingParagraph => Command::EnsureTrailingParagraph,
            Self::InsertTable { rows, cols } => Command::InsertTable { rows, cols },
            Self::InsertTableRow {
                table_id,
                row_index,
                above,
            } => Command::InsertTableRow {
                table_id,
                row_index,
                above,
            },
            Self::DeleteTableRow {
                table_id,
                row_index,
            } => Command::DeleteTableRow {
                table_id,
                row_index,
            },
            Self::InsertTableCol {
                table_id,
                col_index,
                left,
            } => Command::InsertTableCol {
                table_id,
                col_index,
                left,
            },
            Self::DeleteTableCol {
                table_id,
                col_index,
            } => Command::DeleteTableCol {
                table_id,
                col_index,
            },
            Self::MergeCells { table_id } => Command::MergeCells { table_id },
            Self::UnmergeCells { table_id, cell_id } => Command::UnmergeCells { table_id, cell_id },
            Self::SplitCell {
                table_id,
                cell_id,
                direction,
            } => Command::SplitCell {
                table_id,
                cell_id,
                direction: direction.into(),
            },
            Self::SetCellProps { cell_ids, props } => Command::SetCellProps { cell_ids, props },
            Self::SetTableProps { table_id, props } => Command::SetTableProps { table_id, props },
            Self::SetRowProps { row_id, props } => Command::SetRowProps { row_id, props },
            Self::ToggleCellMark { cell_ids, mark } => Command::ToggleCellMark {
                cell_ids,
                mark_type: mark,
            },
            Self::ClearCells { cell_ids } => Command::ClearCells { cell_ids },
            Self::TableNavigate { direction } => Command::TableNavigate {
                direction: direction.into(),
            },
        }
    }
}
