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

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapProjection {
    pub schema: String,
    pub map_id: String,
    pub root_node: String,
    #[serde(default)]
    pub nodes: Vec<MindmapProjectionNode>,
    #[serde(default)]
    pub edges: Vec<MindmapProjectionEdge>,
    #[serde(default)]
    pub layout_hints: Value,
    pub revision: u64,
    pub owner: MindmapProjectionOwner,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapProjectionNode {
    pub node_id: String,
    pub title: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parent_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapProjectionEdge {
    pub edge_id: String,
    pub from_node_id: String,
    pub to_node_id: String,
    pub edge_kind: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum MindmapProjectionOwner {
    RustKernel,
    CompatBlob,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum MindmapProjectionSource {
    Kernel,
    CompatBlob,
    AdapterCache,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapKernelProjection {
    pub schema: String,
    pub document_id: String,
    pub mindmap_id: String,
    pub root_node_id: String,
    pub revision: u64,
    #[serde(default)]
    pub nodes: Vec<MindmapKernelNode>,
    #[serde(default)]
    pub edges: Vec<MindmapKernelEdge>,
    #[serde(default)]
    pub summaries: Vec<MindmapSummary>,
    #[serde(default)]
    pub associative_lines: Vec<MindmapAssociativeLine>,
    #[serde(default)]
    pub layout: Value,
    #[serde(default)]
    pub theme: Value,
    #[serde(default)]
    pub view: Value,
    pub capabilities: MindmapKernelCapabilities,
    pub source: MindmapProjectionSource,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapKernelNode {
    pub node_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parent_id: Option<String>,
    pub text: String,
    #[serde(default)]
    pub order: i64,
    #[serde(default)]
    pub collapsed: bool,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub style: BTreeMap<String, Value>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub refs: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapKernelEdge {
    pub edge_id: String,
    pub from_node_id: String,
    pub to_node_id: String,
    pub edge_kind: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapSummary {
    pub summary_id: String,
    pub parent_node_id: String,
    #[serde(default)]
    pub node_ids: Vec<String>,
    pub text: String,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub style: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapAssociativeLine {
    pub line_id: String,
    pub from_node_id: String,
    pub to_node_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub style: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapKernelCapabilities {
    pub can_edit: bool,
    pub can_edit_text: bool,
    pub can_insert_child: bool,
    pub can_insert_sibling_after: bool,
    pub can_delete_node: bool,
    pub can_move_node: bool,
    pub can_patch_view: bool,
    pub can_set_layout: bool,
    pub can_set_theme: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapAdapterProjection {
    pub schema: String,
    pub runtime: String,
    pub root: Value,
    #[serde(default)]
    pub layout: Value,
    #[serde(default)]
    pub theme: Value,
    #[serde(default)]
    pub theme_config: Value,
    #[serde(default)]
    pub view: Value,
    #[serde(default)]
    pub config: Value,
    #[serde(default)]
    pub compat_payload: Value,
    pub kernel_revision: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum MindmapKernelCommand {
    UpdateText {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
        text: String,
    },
    InsertChild {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        #[serde(rename = "parentNodeId", alias = "parent_node_id", alias = "parentId")]
        parent_node_id: String,
        node: MindmapNodeInput,
    },
    InsertSiblingAfter {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        #[serde(rename = "targetNodeId", alias = "target_node_id")]
        target_node_id: String,
        node: MindmapNodeInput,
    },
    DeleteNode {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
    },
    MoveNode {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
        #[serde(
            rename = "newParentNodeId",
            alias = "new_parent_node_id",
            alias = "newParentId"
        )]
        new_parent_node_id: String,
        #[serde(rename = "order", alias = "sortOrder", alias = "sort_order")]
        order: Option<i64>,
    },
    PatchView {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        patch: Value,
    },
    SetLayout {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        layout: Value,
    },
    SetTheme {
        #[serde(rename = "mindmapId", alias = "mindmap_id", alias = "mapId")]
        mindmap_id: String,
        theme: Value,
        #[serde(default)]
        #[serde(rename = "themeConfig", alias = "theme_config")]
        theme_config: Value,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum MindmapCommand {
    CreateNode {
        #[serde(rename = "mapId", alias = "map_id")]
        map_id: String,
        #[serde(rename = "parentId", alias = "parent_id")]
        parent_id: String,
        node: MindmapNodeInput,
    },
    RenameNode {
        #[serde(rename = "mapId", alias = "map_id")]
        map_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
        title: String,
    },
    MoveNode {
        #[serde(rename = "mapId", alias = "map_id")]
        map_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
        #[serde(rename = "newParentId", alias = "new_parent_id")]
        new_parent_id: String,
        #[serde(rename = "sortOrder", alias = "sort_order")]
        sort_order: Option<i64>,
    },
    DeleteNode {
        #[serde(rename = "mapId", alias = "map_id")]
        map_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
    },
    SetLayout {
        #[serde(rename = "mapId", alias = "map_id")]
        map_id: String,
        #[serde(rename = "layoutHints", alias = "layout_hints")]
        layout_hints: Value,
    },
    AttachPageRef {
        #[serde(rename = "mapId", alias = "map_id")]
        map_id: String,
        #[serde(rename = "nodeId", alias = "node_id")]
        node_id: String,
        #[serde(rename = "pageId", alias = "page_id")]
        page_id: String,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapNodeRef {
    pub kind: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub asset_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub file_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub page: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub slide: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub snippet: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapNodeData {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uid: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hyperlink: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub refs: Option<Vec<MindmapNodeRef>>,
    #[serde(flatten, default)]
    pub extra: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MindmapTreeNode {
    pub data: MindmapNodeData,
    #[serde(default)]
    pub children: Vec<MindmapTreeNode>,
    #[serde(flatten, default)]
    pub extra: BTreeMap<String, Value>,
}

/// 兼容 DTO：对应 simple-mind-map / KMind 风格树节点。
/// 长期事实源应使用 `MindmapKernelProjection` 与 kernel command。
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MindmapNodeInput {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uid: Option<String>,
    pub text: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hyperlink: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub refs: Option<Vec<MindmapNodeRef>>,
}

/// 兼容 DTO：用于历史 AI 工具和旧 blob 操作。
/// 新的 leptos-mindmap 写面应优先使用 `MindmapKernelCommand`。
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "op", rename_all = "camelCase")]
pub enum MindmapOp {
    #[serde(alias = "add_child")]
    AddChild {
        #[serde(rename = "parentUid")]
        #[serde(alias = "parent_uid")]
        parent_uid: String,
        node: MindmapNodeInput,
    },
    #[serde(alias = "add_sibling_after")]
    AddSiblingAfter {
        #[serde(rename = "targetUid")]
        #[serde(alias = "target_uid")]
        target_uid: String,
        node: MindmapNodeInput,
    },
    #[serde(alias = "update_node", alias = "updateNode")]
    UpdateText {
        uid: String,
        #[serde(alias = "value")]
        text: String,
    },
    #[serde(alias = "set_link", alias = "set_hyperlink")]
    SetHyperlink {
        #[serde(alias = "id")]
        uid: String,
        #[serde(alias = "url")]
        hyperlink: Option<String>,
    },
    #[serde(alias = "set_refs")]
    SetRefs {
        #[serde(alias = "id")]
        uid: String,
        refs: Vec<MindmapNodeRef>,
    },
    #[serde(alias = "append_note")]
    AppendNote {
        #[serde(alias = "id")]
        uid: String,
        #[serde(alias = "note")]
        markdown: String,
    },
    #[serde(alias = "delete_node")]
    DeleteNode {
        #[serde(alias = "id")]
        uid: String,
    },
}
