use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum EditorBlockType {
    Paragraph,
    Heading,
    BulletListItem,
    NumberedListItem,
    Quote,
    Todo,
    CodeBlock,
    PageReference,
    BlockReference,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ReferenceTokenKind {
    PageReference,
    BlockReference,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ReferenceTokenStrategy {
    DoubleBracket,
    DoubleParen,
    InlineChip,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TextMark {
    Bold,
    Italic,
    Underline,
    Strike,
    Code,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceToken {
    pub kind: ReferenceTokenKind,
    pub strategy: ReferenceTokenStrategy,
    pub target_id: String,
    pub label: Option<String>,
    pub raw_token: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentNodePayload {
    Text {
        text: String,
        #[serde(default)]
        marks: Vec<TextMark>,
    },
    HardBreak,
    ReferenceToken {
        token: ReferenceToken,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ContentNode {
    pub payload: ContentNodePayload,
    #[serde(default)]
    pub attrs: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct BlockProps {
    pub indent: Option<u16>,
    pub heading_level: Option<u8>,
    pub checked: Option<bool>,
    pub collapsed: Option<bool>,
    pub language: Option<String>,
    pub reference_target_id: Option<String>,
    pub reference_label: Option<String>,
    pub reference_token_strategy: Option<ReferenceTokenStrategy>,
    #[serde(default)]
    pub extra: BTreeMap<String, Value>,
}

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

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct EditorBlockDocument {
    pub document_id: String,
    #[serde(default)]
    pub root_block_ids: Vec<String>,
    #[serde(default)]
    pub blocks: Vec<EditorBlock>,
}
