use crate::editor::model::{BlockProps, ContentNode, EditorBlock, EditorBlockType, ReferenceToken};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum EditorCommandKind {
    ReplaceBlock,
    InsertBlockAfter,
    DeleteBlock,
    SplitBlock,
    MergeWithPrevious,
    MoveBlock,
    IndentBlock,
    OutdentBlock,
    ToggleHeadingCollapse,
    AttachReferenceToken,
    DetachReferenceToken,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EditorReplaceBlock {
    pub block_id: String,
    pub block_type: Option<EditorBlockType>,
    pub props: Option<BlockProps>,
    pub content_nodes: Option<Vec<ContentNode>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EditorInsertBlockAfter {
    pub after_block_id: String,
    pub block: EditorBlock,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorDeleteBlock {
    pub block_id: String,
    pub preserve_children: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorSplitBlock {
    pub block_id: String,
    pub content_node_index: u32,
    pub text_offset: Option<u32>,
    pub trailing_block_type: Option<EditorBlockType>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorMergeWithPrevious {
    pub block_id: String,
    pub previous_block_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorMoveBlock {
    pub block_id: String,
    pub parent_block_id: Option<String>,
    pub after_block_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorIndentBlock {
    pub block_id: String,
    pub max_depth: Option<u16>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorOutdentBlock {
    pub block_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorToggleHeadingCollapse {
    pub block_id: String,
    pub collapsed: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorAttachReferenceToken {
    pub block_id: String,
    pub content_node_index: u32,
    pub token: ReferenceToken,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorDetachReferenceToken {
    pub block_id: String,
    pub content_node_index: u32,
    pub target_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum EditorCommand {
    ReplaceBlock(EditorReplaceBlock),
    InsertBlockAfter(EditorInsertBlockAfter),
    DeleteBlock(EditorDeleteBlock),
    SplitBlock(EditorSplitBlock),
    MergeWithPrevious(EditorMergeWithPrevious),
    MoveBlock(EditorMoveBlock),
    IndentBlock(EditorIndentBlock),
    OutdentBlock(EditorOutdentBlock),
    ToggleHeadingCollapse(EditorToggleHeadingCollapse),
    AttachReferenceToken(EditorAttachReferenceToken),
    DetachReferenceToken(EditorDetachReferenceToken),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorKernelCommandMapping {
    pub editor_command: EditorCommandKind,
    pub kernel_command_name: String,
    pub lossy: bool,
    pub notes: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EditorCommandDescriptor {
    pub kind: EditorCommandKind,
    pub label: String,
    pub write_command: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct EditorCommandCatalog {
    #[serde(default)]
    pub commands: Vec<EditorCommandDescriptor>,
    #[serde(default)]
    pub kernel_command_mappings: Vec<EditorKernelCommandMapping>,
}
